Introduction —

Data Pre-Processing

A full inforuction to the project and a description of Kallisto alignment and data filtering/normalization steps can be found in Ss_RNAseq_Data_Processing.rmd.

Data Analysis

Principal component analysis applied to filtered and TMM-normalized expression data identified two developmental clusters that account for 42.6% and 28.9% of expression variability between S. stercoralis developmental stages. The top 10% of genes contributing to PC1 and PC2 are identified and printed as a DataTable.

The limma package ( Ritchie et al 2015, Phipson et al 2016) is used to conduct pairwise differential gene expression analyses between life stages. Here, all unique pairwise comparisons between life stages are displayed as interactive volcano plots and DataTables. In this analysis, genes included in DataTables are corrected for multiple life-stage comparisons. An additional code chunk identifies all genes that are differentially expressed in any way across groups (i.e. an ANOVA analysis across life stages). This set of genes is passed into a clustering analysis that generates a heatmap of differential gene expression across life stages.

Load and Parse Preprocessed Data —

load (file = "../Outputs/SsRNAseq_data_preprocessed")
targets <- SsRNAseq.preprocessed.data$targets
annotations <- SsRNAseq.preprocessed.data$annotations
log2.cpm.filtered.norm <- SsRNAseq.preprocessed.data$log2.cpm.filtered.norm
myDGEList.filtered.norm <-SsRNAseq.preprocessed.data$myDGEList.filtered.norm

rm(SsRNAseq.preprocessed.data)

load(file = "../Strongyloides_RNAseq_Browser/Data/Ss_vDEGList")

Hierarchical Clustering and Principle Components Analysis —

# Introduction to this chunk -----------
# This code chunk starts with filtered and normalized abundance data in a data frame (not tidy).
# It will implement hierarchical clustering and PCA analyses on the data.
# It will plot various graphs and can save them in PDF files.
# Load packages ------
suppressPackageStartupMessages({
  library(tidyverse) # you're familiar with this fromt the past two lectures
  library(ggplot2)
  library(RColorBrewer)
  library(ggdendro)
  library(magrittr)
  library(factoextra)
  library(gridExtra)
  library(cowplot)
  library(dendextend)
})

# Identify variables of interest in study design file ----
group <- factor(targets$group)

# Hierarchical clustering ---------------
# Remember: hierarchical clustering can only work on a data matrix, not a data frame

# Calculate distance matrix
# dist calculates distance between rows, so transpose data so that we get distance between samples.
# how similar are samples from each other
colnames(log2.cpm.filtered.norm)<-targets$group
distance <- dist(t(log2.cpm.filtered.norm), method = "maximum") #other distance methods are "euclidean", maximum", "manhattan", "canberra", "binary" or "minkowski"

# Calculate clusters to visualize differences. This is the hierarchical clustering.
# The methods here include: single (i.e. "friends-of-friends"), complete (i.e. complete linkage), and average (i.e. UPGMA). Here's a comparison of different types: https://en.wikipedia.org/wiki/UPGMA#Comparison_with_other_linkages
clusters <- hclust(distance, method = "complete") #other agglomeration methods are "ward.D", "ward.D2", "single", "complete", "average", "mcquitty", "median", or "centroid"
dend <- as.dendrogram(clusters) 

p1<-dend %>% 
  dendextend::set("branches_k_color", k = 5) %>% 
  dendextend::set("hang_leaves", c(0.1)) %>% 
  dendextend::set("labels_cex", c(0.5)) %>%
  dendextend::set("labels_colors", k = 5) %>% 
  dendextend::set("branches_lwd", c(0.7)) %>% 
  
  as.ggdend %>%
  ggplot (offset_labels = -0.5) +
  theme_dendro() +
  ylim(0, max(get_branches_heights(dend))) +
  labs(title = "Hierarchical Cluster Dendrogram ",
       subtitle = "filtered, TMM normalized",
       y = "Distance",
       x = "Life stage") +
  coord_fixed(1/2) +
  theme(axis.title.x = element_text(color = "black"),
        axis.title.y = element_text(angle = 90),
        axis.text.y = element_text(angle = 0),
        axis.line.y = element_line(color = "black"),
        axis.ticks.y = element_line(color = "black"),
        axis.ticks.length.y = unit(2, "mm"))
p1


# Principal component analysis (PCA) -------------
# this also works on a data matrix, not a data frame
pca.res <- prcomp(t(log2.cpm.filtered.norm), scale.=F, retx=T)
summary(pca.res) # Prints variance summary for all principal components.
Importance of components:
                            PC1      PC2      PC3      PC4      PC5      PC6      PC7
Standard deviation     124.8775 105.7601 60.28878 53.37388 41.40321 26.13575 20.28184
Proportion of Variance   0.4219   0.3026  0.09833  0.07707  0.04638  0.01848  0.01113
Cumulative Proportion    0.4219   0.7245  0.82282  0.89989  0.94627  0.96475  0.97588
                            PC8      PC9    PC10    PC11    PC12    PC13    PC14    PC15
Standard deviation     13.66342 10.37244 9.93966 9.00105 8.75746 7.89616 7.37748 7.04007
Proportion of Variance  0.00505  0.00291 0.00267 0.00219 0.00207 0.00169 0.00147 0.00134
Cumulative Proportion   0.98093  0.98384 0.98651 0.98870 0.99078 0.99246 0.99394 0.99528
                          PC16    PC17    PC18    PC19    PC20      PC21
Standard deviation     6.52221 6.41971 6.00467 5.58038 4.85766 6.462e-14
Proportion of Variance 0.00115 0.00111 0.00098 0.00084 0.00064 0.000e+00
Cumulative Proportion  0.99643 0.99754 0.99852 0.99936 1.00000 1.000e+00
#pca.res$rotation #$rotation shows you how much each gene influenced each PC (called 'scores')
#pca.res$x # 'x' shows you how much each sample influenced each PC (called 'loadings')
#note that these have a magnitude and a direction (this is the basis for making a PCA plot)
## This generates a screeplot: a standard way to view eigenvalues for each PCA. Shows the proportion of variance accounted for by each PC. Plotting only the first 10 dimensions.
p2<-fviz_eig(pca.res,
             barcolor = brewer.pal(8,"Pastel2")[8],
             barfill = brewer.pal(8,"Pastel2")[8],
             linecolor = "black",
             main = "Scree plot: proportion of variance accounted for by each principal component",
             ggtheme = theme_bw()) 
p2


pc.var<-pca.res$sdev^2 # sdev^2 captures these eigenvalues from the PCA result
pc.per<-round(pc.var/sum(pc.var)*100, 1) # we can then use these eigenvalues to calculate the percentage variance explained by each PC

# Visualize the PCA result ------------------
#lets first plot any two PCs against each other
#We know how much each sample contributes to each PC (loadings), so let's plot
pca.res.df <- as_tibble(pca.res$x)

# Plotting PC1 and PC2
p3<-ggplot(pca.res.df) +
  aes(x=PC1, y=PC2, label=targets$group, 
      fill = targets$group,
      color = targets$group
  ) +
  geom_point(size=4, shape= 21, color = "black", alpha = 0.5) +
  #geom_label(color = "black", size = 2) +
  scale_fill_brewer(palette = "Set2") +
  scale_color_brewer(palette = "Set2", guide = FALSE) +
  #stat_ellipse() +
  xlab(paste0("PC1 (",pc.per[1],"%",")")) + 
  ylab(paste0("PC2 (",pc.per[2],"%",")")) +
  labs(title="Principal Components Analysis of S. stercoralis RNAseq Samples",
       sub = "Note: analysis is blind to life stage identity.",
       fill = "Life Stage") +
  scale_x_continuous(expand = c(.3, .3)) +
  scale_y_continuous(expand = c(.3, .3)) +
  coord_fixed() +
  theme_bw()
p3


# A guess at the identity of PC1
p4<-ggplot(pca.res.df) +
  aes(x=PC1, y=PC2, label=targets$group,
      color = factor(targets$Maturity),
      fill = factor(targets$Maturity)
  ) +
  #geom_point(size=4) +
  #stat_ellipse() +
  geom_label(color = "black", size = 2, alpha = 0.7) +
  scale_fill_manual(values = brewer.pal(8,"Set2")) +
  scale_color_manual(values = brewer.pal(8,"Set2"), guide = FALSE) +
  xlab(paste0("PC1 (",pc.per[1],"%",")")) + 
  ylab(paste0("PC2 (",pc.per[2],"%",")")) +
  labs(subtitle="Potential PC1 identity: Adults vs Larvae",
       fill = "Maturity") +
  scale_x_continuous(expand = c(.3, .3)) +
  scale_y_continuous(expand = c(.3, .3)) +
  coord_fixed() +
  theme_bw()
p4


# A guess at the identity of PC2
p5<-ggplot(pca.res.df) +
  aes(x=PC1, y=PC2, label=targets$group, 
      color = factor(targets$Infectious),
      fill = factor(targets$Infectious)
  ) +
  #geom_point(size=4) +
  #stat_ellipse() +
  geom_label(color = "black", size = 2, alpha = 0.7) +
  scale_fill_manual(values = brewer.pal(8,"Set2")[3:4]) +
  scale_color_manual(values = brewer.pal(8,"Set2")[3:4], guide = FALSE) +
  xlab(paste0("PC1 (",pc.per[1],"%",")")) + 
  ylab(paste0("PC2 (",pc.per[2],"%",")")) +
  labs(subtitle="Potential PC2 identity: Host-seeking/dwelling",
       fill = 'Infectivity Stage') +
  scale_x_continuous(expand = c(.3, .3)) +
  scale_y_continuous(expand = c(.3, .3)) +
  coord_fixed() +
  theme_bw()
p5


# Create a PCA 'small multiples' chart ----
pca.res.df <- pca.res$x[,1:6] %>% 
  as_tibble() %>%
  add_column(sample = targets$sample,
             group = group,
             maturity = factor(targets$Maturity),
             infectious = factor(targets$Infectious))

pca.pivot <- pivot_longer(pca.res.df, # dataframe to be pivoted
                          cols = PC1:PC6, # column names to be stored as a SINGLE variable
                          names_to = "PC", # name of that new variable (column)
                          values_to = "loadings") # name of new variable (column) storing all the values (data)
PC1<-subset(pca.pivot, PC == "PC1")
PC2 <-subset(pca.pivot, PC == "PC2")
#PC3 <- subset(pca.pivot, PC == "PC3")
#PC4 <- subset(pca.pivot, PC == "PC4")

p6<-ggplot(pca.pivot) +
  aes(x=sample, y=loadings) + # you could iteratively 'paint' different covariates onto this plot using the 'fill' aes
  geom_bar(stat="identity", fill = brewer.pal(8,"Pastel2")[8]) +
  scale_fill_brewer(palette = "Set2") +
  facet_wrap(~PC) +
  geom_bar(data = PC1, stat = "identity", aes(fill = maturity)) +
  geom_bar(data = PC2, stat = "identity", aes(fill = infectious)) +
  labs(title="PCA 'small multiples' plot",
       fill = "Life Stage Groups",
       caption=paste0("produced on ", Sys.time())) +
  scale_x_discrete(limits = targets$sample, labels = targets$group) +
  theme_bw() +
  coord_flip()
p6


# Graph all the plots generated in this script ----
# Plot all the plots on a grid
# p7<- grid.arrange(p1,p2,p3,p4, p5,p6,ncol = 4,
#             layout_matrix = cbind(c(1,1,1,2,2,2),c(3,3,4,4,5,5),c(6,6,6,6,6,6), c(6,6,6,6,6,6)))
# ggsave("Strongyloides stercoralis RNAseq Multivariate Analysis.pdf", 
#        plot = p7, 
#        device = "pdf",
#        #height = 17,
#        width = 22,
#        path = '../Outputs/'

Genes Contributing to PC Identity —

# Introduction to this chunk ----
# This chunk provides additional analysis of the principal components, in order to determine which genes are influencing the identified PCs.

# Use pca.res$rotation to select genes influencing PC1-6 ----
myscores.df <- pca.res$rotation[,1:6] %>% 
  as_tibble(rownames = "geneID") %>%
  pivot_longer(cols = -geneID, names_to = "PC", values_to = "scores") %>%
  dplyr::mutate(abs_scores = abs(scores)) %>%
  group_by(PC) %>%
  slice_max(abs_scores, prop = .1) # get top 10% of genes in all PCs

# Pull out genes that are the top 10% of contributors (in any direction) to PC1 and PC2. Annotate.
myscores.Top10 <- myscores.df %>%
  dplyr::filter(PC == "PC1" | PC == "PC2") %>%
  pivot_wider(id_cols = geneID,
              names_from = PC,
              values_from = scores) %>%
  dplyr::mutate(PC1_id = case_when(PC1 > 0 ~ "larval",
                                   PC1 < 0 ~ "adult",
                                   is.na(PC1) ~ "--")) %>%  
  dplyr::mutate(PC2_id = case_when(PC2 > 0 ~ "parasite",
                                   PC2 < 0 ~ "non_parasite",
                                   is.na(PC2) ~ "--")) %>%  
  dplyr::left_join(.,(rownames_to_column(annotations, var = "geneID")), by = "geneID") %>%
  dplyr::relocate(UniProtKB, Description, InterPro, GO_term, Ce_geneID, Ce_percent_homology, .after = PC2_id)

# Make Interactive Plot
myscores.Top10.interactive <- myscores.Top10 %>%
  DT::datatable(extensions = c('KeyTable', "FixedHeader"),
                rownames = FALSE,
                caption = htmltools::tags$caption(
                  style = 'caption-side: top; text-align: left;',
                  htmltools::tags$b('Top 10% of Genes Contributing to PC1 and PC2'),
                  htmltools::tags$br(),
                  'Search for PC1_id/PC2_id values to filter results'),
                options = list(keys = TRUE,
                               autoWidth = TRUE,
                               scrollX = TRUE,
                               order = list(1, 'desc'),
                               searchHighlight = TRUE, 
                               pageLength = 10, 
                               lengthMenu = c("10", "25", "50", "100"))) %>%
  DT::formatRound(columns=c(2:3), digits=3)

myscores.Top10.interactive

Heatmap of Gene Expression Across Life Stages —

library(pheatmap)
library(RColorBrewer)
# Make a heatmap for all the genes using the Log2CPM values

diffGenes <- v.DEGList.filtered.norm$E %>%
  as_tibble(rownames = "geneID", .name_repair = "unique") %>%
  dplyr::select(!geneID) %>%
  as.matrix()
rownames(diffGenes) <- rownames(v.DEGList.filtered.norm$E)
colnames(diffGenes) <- as.character(v.DEGList.filtered.norm$targets$group)
clustColumns <- hclust(as.dist(1-cor(diffGenes, method="spearman")), method="complete")
clustRows <- hclust(as.dist(1-cor(t(diffGenes), 
                                  method="pearson")), 
                    method="complete") 
par(cex.main=1.2)

showticklabels <- c(TRUE,FALSE)
p<-pheatmap(diffGenes,
            color = RdBu(75),
            cluster_rows = clustRows,
            cluster_cols = clustColumns,
            show_rownames = F,
            scale = "row",
            angle_col = 45,
            main = "Log2 Counts Per Million (CPM) Expression Across Life Stages"
          
)

Differentially Expressed Genes —

# Introduction to this chunk ----
# This chunk uses a variance-stabilized DGEList of filtered and normalized abundance data.
# 
# These data/results are examples, a responsive version of this code is avaliable in a Shiny App.
# 
# Because we have access to biological replicates, we can use statistical tools for differential expression analysis
# Useful reading on differential expression: https://ucdavis-bioinformatics-training.github.io/2018-June-RNA-Seq-Workshop/thursday/DE.html

# Load packages ----
suppressPackageStartupMessages({
  library(tidyverse)
  library(limma) # differential gene expression using linear modeling
  library(edgeR)
  library(gt) 
  library(DT) 
  library(plotly)
  library(ggthemes)
  library(RColorBrewer)
  source("../Strongyloides_RNAseq_Browser/Server/theme_Publication.R")
})

diffGenes.df <- v.DEGList.filtered.norm$E %>%
  as_tibble(rownames = "geneID", .name_repair = "unique")

# Set Expression threshold values for plotting and saving DEGs ----
adj.P.thresh <- 0.05
lfc.thresh <- 1 

group <- factor(targets$group)
design <- model.matrix(~0 + group) # no intercept/blocking for matrix, comparisons across group
colnames(design) <- levels(group)

# Fit a linear model to the data ----
fit <- lmFit(v.DEGList.filtered.norm, design)

# As an example, generate comparison matrix for a pairwise comparison ----
# iL3s vs FLF
# Note that the target/contrast goups will be divided by the number of life 
# stage groups e.g. PF+FLF/2 - iL3+iL3a+pfL1+ppL1+ppL3/5
comparison <- c('(iL3)-(FLF)')

targetStage<- comparison %>%
  str_split(pattern="-", simplify = T) %>%
  .[,1] %>%
  gsub("(", "", ., fixed = TRUE) %>%
  gsub(")", "", ., fixed = TRUE) %>%
  str_split(pattern = "\\+", simplify = T)

contrastStage<-comparison %>%
  str_split(pattern="-", simplify = T) %>%
  .[,2] %>%
  gsub("(", "", ., fixed = TRUE) %>%
  gsub(")", "", ., fixed = TRUE)  %>%
  str_split(pattern = "\\+", simplify = T)

comparison<- sapply(seq_along(comparison),function(x){
  tS <- as.vector(targetStage[x,]) %>%
    .[. != ""] 
  cS <- as.vector(contrastStage[x,]) %>%
    .[. != ""] 
  paste(paste0(tS, 
               collapse = "+") %>%
          paste0("(",.,")/",length(tS)),
        paste0(cS, 
               collapse = "+") %>%
          paste0("(",.,")/",length(cS)),
        sep = "-")
  
})

# Generate contrast matrix ----
contrast.matrix <- makeContrasts(contrasts = comparison,
                                 levels=design)

# extract the linear model fit -----
fits <- contrasts.fit(fit, contrast.matrix)
# empirical bayes smoothing of gene-wise standard deviations provides increased power (see: https://www.degruyter.com/doi/10.2202/1544-6115.1027)
ebFit <- eBayes(fits)

# Pull out the DEGs that pass a specific threshold for all pairwise comparisons ----
# Adjust for multiple comparisons using method = global. 
results <- decideTests(ebFit, method="global", adjust.method="BH", p.value = adj.P.thresh)

recode01<- function(x){
  case_when(x == 1 ~ "Up",
            x == -1 ~ "Down",
            x == 0 ~ "NotSig")
}
diffDesc <- results %>%
  as_tibble(rownames = "geneID") %>%
  dplyr::mutate(across(-geneID, unclass)) %>%
  dplyr::mutate(across(where(is.double), recode01))

# Function that identifies top DEGs between a specific contrast ----
calc_DEG_tbl <- function (ebFit, coef) {
  myTopHits.df <- limma::topTable(ebFit, adjust ="BH", 
                                  coef=coef, number=40000, 
                                  sort.by="logFC") %>%
    as_tibble(rownames = "geneID") %>%
    dplyr::rename(tStatistic = t, LogOdds = B, BH.adj.P.Val = adj.P.Val) %>%
    dplyr::relocate(UniProtKB, Description, InterPro, GO_term,  Str_geneID, Str_WBgeneID, Str_percent_homology, Ce_geneID, Ce_percent_homology, .after = LogOdds)
  
  myTopHits.df
}

list.myTopHits.df <- sapply(comparison, function(y){
  calc_DEG_tbl(ebFit, y)}, 
  simplify = FALSE, 
  USE.NAMES = TRUE)

list.myTopHits.df <- sapply(comparison, function(y){
  list.myTopHits.df[[y]] %>%
    dplyr::select(geneID, 
                  logFC, 
                  BH.adj.P.Val:Ce_percent_homology)},
  simplify = FALSE, 
  USE.NAMES = TRUE)

# Get log2CPM values and threshold information for genes of interest
list.myTopHits.df <- sapply(seq_along(comparison), function(y){
  tS<- targetStage[y,][targetStage[y,]!=""]
  cS<- contrastStage[y,][contrastStage[y,]!=""]
  
  concat_name <- function(x) {
    ifelse(x == "target", 
           paste(tS, collapse = "+"), 
           paste(cS, collapse = "+"))
  }
  
  groupAvgs <- diffGenes.df %>%
    dplyr::select(geneID, starts_with(paste0(tS,"-")), 
                  starts_with(paste0(cS,"-"))) %>%
    pivot_longer(cols = -geneID, names_to = c("group","sample"), values_to = "CPM",
                 names_sep = "-") %>%
    dplyr::mutate(contrastID = if_else(group %in% tS,"target", "contrast")) %>%
    group_by(geneID, contrastID) %>%
    dplyr::select(-sample) %>%
    summarize(mean = mean(CPM), .groups = "drop_last") %>%
    pivot_wider(names_from = contrastID, values_from = mean) %>%
    dplyr::relocate(contrast, .after = target) %>%
    dplyr::rename_with(concat_name, -geneID) %>%
    dplyr::rename_with(.cols =-geneID, .fn = ~ paste0("avg_(",.x,")"))
  
  diffGenes.df %>%
    dplyr::select(geneID, starts_with(paste0(tS,"-")), 
                  starts_with(paste0(cS,"-"))) %>%
    left_join(groupAvgs, by = "geneID") %>%
    left_join(list.myTopHits.df[[y]],., by = "geneID") %>%
    left_join(dplyr::select(diffDesc,geneID,comparison[y]), by = "geneID") %>%
    dplyr::rename(DEG_Desc=comparison[y]) %>%
    dplyr::relocate(DEG_Desc) %>%
    dplyr::relocate(logFC:Ce_percent_homology, .after = last_col())
  
},
simplify = FALSE)

comparison <- gsub("/[0-9]*","", comparison)
names(list.myTopHits.df) <- comparison

list.myTopHits.df <- sapply(comparison, function(y){
  list.myTopHits.df[[y]] %>%
    dplyr::mutate(DEG_Desc = case_when(DEG_Desc == "Up" ~ paste0("Up in ", str_split(y,'-',simplify = T)[1,1]),
                                       DEG_Desc == "Down" ~ paste0("Down in ", str_split(y,'-',simplify = T)[1,1]),
                                       DEG_Desc == "NotSig" ~ "NotSig")) 
},
simplify = FALSE, 
USE.NAMES = TRUE)

# PC1 Volcano Plot and Interactive Table ----
vplot1 <- ggplot(list.myTopHits.df[[1]]) +
  aes(y=-log10(BH.adj.P.Val), x=logFC, text = paste(geneID, "<br>",
                                                    "logFC:", round(logFC, digits = 2), "<br>",
                                                    "p-val:", format(BH.adj.P.Val, digits = 3, scientific = TRUE))) +
  geom_point(size=2) +
  geom_hline(yintercept = -log10(adj.P.thresh), 
             linetype="longdash", 
             colour="grey", 
             size=1) + 
  geom_vline(xintercept = lfc.thresh, 
             linetype="longdash", 
             colour="#BE684D", 
             size=1) +
  geom_vline(xintercept = -lfc.thresh, 
             linetype="longdash", 
             colour="#2C467A", 
             size=1) +
  labs(title = paste0('Pairwise Comparison: ',
                      gsub('-',
                           ' vs ',
                           comparison[1])),
       subtitle = paste0("grey line: p = ",
                         adj.P.thresh, "; colored lines: log-fold change = ", lfc.thresh),
       color = "GeneIDs") +
  theme_Publication() 
vplot1


# Interactive Tables
y<- 1
tS<- targetStage[y,][targetStage[y,]!=""]
cS<- contrastStage[y,][contrastStage[y,]!=""]

sample.num.tS <- sapply(tS, function(x) {colSums(v.DEGList.filtered.norm$design)[[x]]}) %>% sum()
sample.num.cS <- sapply(cS, function(x) {colSums(v.DEGList.filtered.norm$design)[[x]]}) %>% sum()


n_num_cols <- sample.num.tS + sample.num.cS + 5
LS.datatable <- list.myTopHits.df[[y]] %>%
  DT::datatable(rownames = FALSE,
                caption = htmltools::tags$caption(
                  style = 'caption-side: top; text-align: left; color: black',
                  htmltools::tags$b('Differentially Expressed Genes in', 
                                    htmltools::tags$em('S. stercoralis'), 
                                    gsub('-',' vs ',comparison[y])),
                  htmltools::tags$br(),
                  "Threshold: p < ",
                  adj.P.thresh, "; log-fold change > ",
                  lfc.thresh,
                  htmltools::tags$br(),
                  'Values = log2 counts per million'),
                options = list(autoWidth = TRUE,
                               scrollX = TRUE,
                               scrollY = '300px',
                               scrollCollapse = TRUE,
                               order = list(n_num_cols-1, 
                                            'desc'),
                               searchHighlight = TRUE, 
                               pageLength = 25, 
                               lengthMenu = c("5",
                                              "10",
                                              "25",
                                              "50",
                                              "100"),
                               columnDefs = list(
                                 list(
                                   targets = ((n_num_cols + 
                                                 1)),
                                   render = JS(
                                     "function(data, row) {",
                                     "data.toExponential(1);",
                                     "}")
                                 ),
                                 list(
                                   targets = ((n_num_cols + 
                                                 4):(n_num_cols + 
                                                       5)),
                                   render = JS(
                                     "function(data, type, row, meta) {",
                                     "return type === 'display' && data.length > 20 ?",
                                     "'<span title=\"' + data + '\">' + data.substr(0, 20) + '...</span>' : data;",
                                     "}")
                                 ),
                                 list(targets = "_all",
                                      class="dt-right")
                               )
                               
                )) 
LS.datatable <- LS.datatable %>%
  DT::formatRound(columns=c(3:n_num_cols), 
                  digits=3)

LS.datatable <- LS.datatable %>%
  DT::formatRound(columns=c(n_num_cols+2, 
                            n_num_cols+8), 
                  digits=2)

LS.datatable <- LS.datatable %>%
  DT::formatSignif(columns=c(n_num_cols+1), 
                   digits=3)

LS.datatable

NA

Benchmarking


suppressPackageStartupMessages({
  library(openxlsx)
  library(tidyverse)
  library(ggplot2)
})
# Load Hunt Dataset: iL3 vs FLF comparison
temp.dat <-  read.xlsx ("../Benchmarking/ng.3495-S5.xlsx", 
                        sheet = 5, startRow = 2)

Hunt.dat <- tibble(geneID = temp.dat$X2, logFC = temp.dat$logFC)
rm(temp.dat)

# Rename Results of iL3 vs FLF comparison from Browser
Browser.dat <- list.myTopHits.df$`(iL3)-(FLF)` %>%
  dplyr::select(geneID, logFC)

print(paste('Total number of genes in Hunt *et al* 2016 iL3 vs FLF comparison tab:',nrow(Hunt.dat)))
[1] "Total number of genes in Hunt *et al* 2016 iL3 vs FLF comparison tab: 11188"
print(paste('Total number of genes in Str-RNAseq Browser iL3 vs FLF output file:', nrow(Browser.dat))) 
[1] "Total number of genes in Str-RNAseq Browser iL3 vs FLF output file: 12381"
# The plot below takes the genes with LogFC results in both the Browser and Hunt databases, and plots the two sets against each other. 
plotting.all <- inner_join(Browser.dat, Hunt.dat, by = "geneID")
ggplot(plotting.all, aes(x = logFC.x, y = logFC.y)) +
  geom_smooth(method=lm, color = 'red', formula = "y ~ x") +
  geom_point(shape=16, size=3, alpha = 0.8) +
  labs(title = "All Overlapping Genes: Str-Browser vs Hunt Data",
       subtitle = "iL3 vs FLF",
       caption = "points = genes; red line = linear model (y ~ x)",
       x = "Str-Browser LogFC",
       y = "Hunt LogFC") +
  coord_equal() +
  theme_bw()

NA
NA

Cluster DEGs into functional modules —

Functional Enrichment Analysis —

# Load packages ----
suppressPackageStartupMessages({
  library(tidyverse)
  library(limma)
  library(openxlsx)
  library(gplots) #for heatmaps
  library(DT) #interactive and searchable tables of our GSEA results
  library(GSEABase) #functions and methods for Gene Set Enrichment Analysis
  library(Biobase) #base functions for bioconductor; required by GSEABase
  library(GSVA) #Gene Set Variation Analysis, a non-parametric and unsupervised method for estimating variation of gene set enrichment across samples.
  library(gprofiler2) #tools for accessing the GO enrichment results using g:Profiler web resources
  library(clusterProfiler) # provides a suite of tools for functional enrichment analysis
  library(msigdbr) # access to msigdb collections directly within R
  library(enrichplot) # great for making the standard GSEA enrichment plots
})
# Pick a pairwise comparison
yy <- 1

# Carry out GO enrichment using gProfiler2 ----
# GO enrichment requires a pre-selected set of genes. Can use multiple criteria to do that initial selection.
# The GO terms I'm accessing using the gost are from Hunt et al 2016, I believe.

# # PC1 TopTable Results
# enriched.set.pos <-list.myTopHits.df[[yy]] %>% 
#     slice_max(logFC, prop = .1) # get top 10% of genes
# 
# enriched.set.neg <- list.myTopHits.df[[yy]] %>% 
#     slice_min(logFC, prop = .1) # get top 10% of genes
# 
# gost.res.pos <- gost(list(Target_Upregulated = enriched.set.pos$geneID), organism = "ststerprjeb528", correction_method = "fdr")
# gostplot(gost.res.pos, interactive = T, capped = T)
# 
# gost.res.neg <- gost(list(Target_Downregulated_Genes = enriched.set.neg$geneID), organism = "ststerprjeb528", correction_method = "fdr")
# gostplot(gost.res.neg, interactive = T, capped = T)

# Perform GSEA using clusterProfiler ----
# Which library to use for implementation? As per https://academic.oup.com/bib/advance-article/doi/10.1093/bib/bbz158/5722384: "For expression-based EA on the full expression matrix...When given raw read counts, we recommend to apply a VST such as voom [39] to arrive at library-size normalized logCPMs."
# For testing self-contained null hypothesis (test for association of any gene in the set with the phenotype), use ROAST
# For testing competitive null hypothesis (test for excess of differential expression in a gene set relative to genes outside the set) - **their recommendation**, use PADOG or SAFE?
# 
# Ability to do this depends on the availability of gene sets. Major databases (e.g. msigdb don't seem to have stercoralis information. They do have C. elegans gene sets, but I'm not convinced the homology information is good enough for the comparison to be unbiased/meaningful. 
# 
# Although the Lok lab did GSEA in Ramanathan *et al* 2011 (PMID: 21572524), using C. elegans homologs are two manually compiled gene sets: 
# 1) 31 genes with products known to be immunoreactive in *S. stercoralis*-infected humans
# 2) 42 putatively identified heat shock proteins
# Of course, this is before the genome was sequenced, so these genes don't have associated SSTP numbers. 
# 
# In Hunt et al 2016, there is an Ensembl Compara protein family set
# Note that this uses specific transcript information, which I throw out. 
# (e.g. SSTP_0001137400.2 is recoded as SSTP_0001137400)
ensComp.geneIDs <- read.xlsx ("../Data/Hunt_Parasite_Ensembl_Compara.xlsx", 
                              sheet = 1) %>%
  as_tibble() %>%
  dplyr::select(-Family.members) %>%
  pivot_longer(cols = -Compara.family.id, values_to = "geneID") %>%
  dplyr::select(-name) %>%
  dplyr::filter(grepl("SSTP_", geneID))

ensComp.geneIDs$geneID <- str_remove_all(ensComp.geneIDs$geneID, ".[0-9]$")

# Compare these genes to the list of genes in our filtered, normalized list ----
# 
compara.exclusive <- unique(ensComp.geneIDs$geneID) %>%
  as_tibble_col(column_name = "geneID") %>%
  dplyr::anti_join(diffGenes.df, by = "geneID")
nrow(compara.exclusive)
[1] 443
compara.absent <- unique(ensComp.geneIDs$geneID) %>%
  as_tibble_col(column_name = "geneID") %>%
  dplyr::anti_join(diffGenes.df,., by = "geneID") %>%
  dplyr::select(geneID)
nrow(compara.absent)
[1] 1357
# How many genes have associated GO terms? ----
GO.present <- list.myTopHits.df[[yy]]$GO_term %>%
  gsub("NA", NA,.) %>%
  as_tibble_col(column_name = "GO_Term") %>%
  tibble(geneID = list.myTopHits.df[[yy]]$geneID,.) %>%
  dplyr::filter(!is.na(GO_Term))
nrow(GO.present)
[1] 7181
# Are any of these genes part of those not found in the compara dataset? ---- 
GO.present.Compara.absent <- dplyr::semi_join(GO.present, compara.absent, by = "geneID")
nrow(GO.present.Compara.absent)
[1] 538
# Make a list of genes
ensComp.familyIDs <- read.xlsx ("../Data/Hunt_Parasite_Ensembl_Compara.xlsx", 
                                sheet = 2,
                                cols = c(1,4:6)) %>%
  as_tibble() %>%
  dplyr::mutate(Family_Description = dplyr::coalesce(.$Description, 
                                                     .$`Top.product.(members.with.hit)`, 
                                                     .$`Interpro.top.hit.(members.with.hit)`)
  ) %>%
  dplyr::select(Compara.family.id, Family_Description)

ensComp <- left_join(ensComp.geneIDs, ensComp.familyIDs, by = "Compara.family.id") %>%
  dplyr::select(-Compara.family.id) %>%
  dplyr::rename(gs_name = Family_Description, gene_symbol = geneID) %>%
  dplyr::relocate(gs_name, gene_symbol)

rm(ensComp.geneIDs, ensComp.familyIDs)

# Save database of parasite Gene Sets for import into Shiny App. ----

# Filter out genes that aren't part of our RNAseq dataset
genelist <- v.DEGList.filtered.norm$genes %>%
  rownames_to_column(var = "geneID") %>%
  dplyr::select(geneID)
ensComp<- ensComp %>%
  dplyr::rename(geneID = gene_symbol) %>%
  left_join(genelist, ., by = "geneID") %>%
  dplyr::relocate(gs_name, geneID)

save(ensComp,
     file = "../Outputs/parasiteGeneSets")

# Generate rank ordered list of genes ----
mydata.df.sub <- dplyr::select(list.myTopHits.df[[yy]], geneID, logFC)
mydata.gsea <- mydata.df.sub$logFC
names(mydata.gsea) <- as.character(mydata.df.sub$geneID)
mydata.gsea <- sort(mydata.gsea, decreasing = TRUE)

# run GSEA using the 'GSEA' function from clusterProfiler
# Given a priori defined set of gene S (e.g., genes shareing the same DO category), the goal of GSEA is to determine whether the members of S are randomly distributed throughout the ranked gene list (L) or primarily found at the top or bottom.
# There are three key elements of the GSEA method:
# **Calculation of an Enrichment Score.**
# The enrichment score (ES) represent the degree to which a set S is over-represented at the top or bottom of the ranked list L. The score is calculated by walking down the list L, increasing a running-sum statistic when we encounter a gene in S and decreasing when it is not. The magnitude of the increment depends on the gene statistics (e.g., correlation of the gene with phenotype). The ES is the maximum deviation from zero encountered in the random walk; it corresponds to a weighted Kolmogorov-Smirnov-like statistic (Subramanian et al. 2005).
# **Esimation of Significance Level of ES.**
# The p-value of the ES is calculated using permutation test. Specifically, we permute the gene labels of the gene list L and recompute the ES of the gene set for the permutated data, which generate a null distribution for the ES. The p-value of the observed ES is then calculated relative to this null distribution.
# **Adjustment for Multiple Hypothesis Testing.**
# When the entire gene sets were evaluated, DOSE adjust the estimated significance level to account for multiple hypothesis testing and also q-values were calculated for FDR control.
myGSEA.res <- GSEA(mydata.gsea, TERM2GENE=ensComp, verbose=FALSE)
There are ties in the preranked stats (0.28% of the list).
The order of those tied genes will be arbitrary, which may produce unexpected results.
myGSEA.df <- as_tibble(myGSEA.res@result)

myGSEA.tbl<-as_tibble(myGSEA.res@result) %>%
  dplyr::select(-c(Description, pvalue, enrichmentScore)) %>%
  dplyr::rename(normalized_EnrichmentScore = NES)

# view results as an interactive table
enrichment.DT <- datatable(myGSEA.tbl, 
                           rownames = TRUE,
                           caption =  htmltools::tags$caption(
                             style = 'caption-side: top; text-align: left; color: black',
                             htmltools::tags$b('Gene Families Enriched in ', 
                                               gsub('-',' vs ',
                                                    names(list.myTopHits.df)[[yy]]))
                           ),
                           options = list(
                             autoWidth = TRUE,
                             scrollX = TRUE,
                             #scrollY = '800px',
                             scrollCollapse = TRUE,
                             searchHighlight = TRUE, 
                             order = list(3, 'desc'),
                             pageLength = 25, 
                             lengthMenu = c("5",
                                            "10",
                                            "25",
                                            "50",
                                            "100"),
                             columnDefs = list(
                               list(targets = "_all",
                                    class="dt-right")))) %>%
  formatRound(columns=c(3,5:6), digits=2) %>%
  formatRound(columns=c(4), digits=4)
enrichment.DT


# create enrichment plots using the enrichplot package
# gseaplot2(myGSEA.res, 
#           geneSetID = 3, #can choose multiple signatures to overlay in this plot
#           pvalue_table = FALSE, #can set this to FALSE for a cleaner plot
#           title = "SCP/TAP Gene Set") #can also turn off this title

# add a variable to this result that matches enrichment direction with phenotype
myGSEA.df <- myGSEA.df %>%
  mutate(life_stage = case_when(
    NES > 0 ~ str_split(names(list.myTopHits.df)[[yy]],'-',simplify = T)[1,1],
    NES < 0 ~ str_split(names(list.myTopHits.df)[[yy]],'-',simplify = T)[1,2]))

myGSEA.df$ID <- myGSEA.df$ID %>%
  word(sep = ',') %>%
  #word(sep = '/') %>%
  word(sep = ' and')

# create 'bubble plot' to summarize y signatures across x phenotypes
ggplot(myGSEA.df, aes(x=life_stage, y=ID)) + 
  geom_point(aes(size=setSize, color = NES, alpha=-log10(p.adjust))) +
  scale_color_gradient(low="blue", high="red") +
  labs(title = paste0('Gene Families Enriched in ', 
                      gsub('-',' vs ',
                           names(list.myTopHits.df)[[yy]])),
       subtitle = 'NES = Normalized Enrichment Score; Gene family assignments 
             from Ensembl Compara dataset defined in Hunt et al 2016',
       x = "Life Stage",
       y = "Family ID") +
  #coord_fixed(1/2) +
  theme_bw() +
  theme(plot.title.position = "plot",
        plot.caption.position = "plot",
        plot.title = element_text(face = "bold",
                                  size = 13, hjust = 0),
        axis.title = element_text(face = "bold",size = 10.4),
         legend.title = element_text(face="bold",size = 10.4),
        aspect.ratio = 3/1)

LS0tCnRpdGxlOiBPZmZsaW5lIERhdGEgQW5hbHlzaXMgb2YgU3Ryb25neWxvaWRlcyByYXR0aSBidWxrIFJOQXNlcSBkYXRhCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgZGZfcHJpbnQ6IHBhZ2VkCiAgICB0b2M6IHllcwogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2M6IHllcwotLS0KCiMgSW50cm9kdWN0aW9uIC0tLQojIyMgRGF0YSBQcmUtUHJvY2Vzc2luZyAgICAKQSBmdWxsIGluZm9ydWN0aW9uIHRvIHRoZSBwcm9qZWN0IGFuZCBhIGRlc2NyaXB0aW9uIG9mIEthbGxpc3RvIGFsaWdubWVudCBhbmQgZGF0YSBmaWx0ZXJpbmcvbm9ybWFsaXphdGlvbiBzdGVwcyBjYW4gYmUgZm91bmQgaW4gYFNzX1JOQXNlcV9EYXRhX1Byb2Nlc3Npbmcucm1kYC4gCgojIyMgRGF0YSBBbmFseXNpcyAgIApQcmluY2lwYWwgY29tcG9uZW50IGFuYWx5c2lzIGFwcGxpZWQgdG8gZmlsdGVyZWQgYW5kIFRNTS1ub3JtYWxpemVkIGV4cHJlc3Npb24gZGF0YSBpZGVudGlmaWVkIHR3byBkZXZlbG9wbWVudGFsIGNsdXN0ZXJzIHRoYXQgYWNjb3VudCBmb3IgNDIuNiUgYW5kIDI4LjklIG9mIGV4cHJlc3Npb24gdmFyaWFiaWxpdHkgYmV0d2VlbiAqUy4gc3RlcmNvcmFsaXMqIGRldmVsb3BtZW50YWwgc3RhZ2VzLiBUaGUgdG9wIDEwJSBvZiBnZW5lcyBjb250cmlidXRpbmcgdG8gUEMxIGFuZCBQQzIgYXJlIGlkZW50aWZpZWQgYW5kIHByaW50ZWQgYXMgYSBEYXRhVGFibGUuCgpUaGUgbGltbWEgcGFja2FnZSAoIFtSaXRjaGllICpldCBhbCogMjAxNV0oaHR0cHM6Ly9wdWJtZWQubmNiaS5ubG0ubmloLmdvdi8yNTYwNTc5Mi8pLCBbUGhpcHNvbiAqZXQgYWwqIDIwMTZdKGh0dHBzOi8vd3d3Lm5jYmkubmxtLm5paC5nb3YvcG1jL2FydGljbGVzL1BNQzUzNzM4MTIvKSkgaXMgdXNlZCB0byBjb25kdWN0IHBhaXJ3aXNlIGRpZmZlcmVudGlhbCBnZW5lIGV4cHJlc3Npb24gYW5hbHlzZXMgYmV0d2VlbiBsaWZlIHN0YWdlcy4gSGVyZSwgYWxsIHVuaXF1ZSBwYWlyd2lzZSBjb21wYXJpc29ucyBiZXR3ZWVuIGxpZmUgc3RhZ2VzIGFyZSBkaXNwbGF5ZWQgYXMgaW50ZXJhY3RpdmUgdm9sY2FubyBwbG90cyBhbmQgRGF0YVRhYmxlcy4gSW4gdGhpcyBhbmFseXNpcywgZ2VuZXMgaW5jbHVkZWQgaW4gRGF0YVRhYmxlcyBhcmUgY29ycmVjdGVkIGZvciBtdWx0aXBsZSBsaWZlLXN0YWdlIGNvbXBhcmlzb25zLiBBbiBhZGRpdGlvbmFsIGNvZGUgY2h1bmsgaWRlbnRpZmllcyBhbGwgZ2VuZXMgdGhhdCBhcmUgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGluIGFueSB3YXkgYWNyb3NzIGdyb3VwcyAoaS5lLiBhbiBBTk9WQSBhbmFseXNpcyBhY3Jvc3MgbGlmZSBzdGFnZXMpLiBUaGlzIHNldCBvZiBnZW5lcyBpcyBwYXNzZWQgaW50byBhIGNsdXN0ZXJpbmcgYW5hbHlzaXMgdGhhdCBnZW5lcmF0ZXMgYSBoZWF0bWFwIG9mIGRpZmZlcmVudGlhbCBnZW5lIGV4cHJlc3Npb24gYWNyb3NzIGxpZmUgc3RhZ2VzLgoKCiMgTG9hZCBhbmQgUGFyc2UgUHJlcHJvY2Vzc2VkIERhdGEgLS0tCmBgYHtyIGxvYWRCYXNlRGF0YX0KbG9hZCAoZmlsZSA9ICIuLi9PdXRwdXRzL1NzUk5Bc2VxX2RhdGFfcHJlcHJvY2Vzc2VkIikKdGFyZ2V0cyA8LSBTc1JOQXNlcS5wcmVwcm9jZXNzZWQuZGF0YSR0YXJnZXRzCmFubm90YXRpb25zIDwtIFNzUk5Bc2VxLnByZXByb2Nlc3NlZC5kYXRhJGFubm90YXRpb25zCmxvZzIuY3BtLmZpbHRlcmVkLm5vcm0gPC0gU3NSTkFzZXEucHJlcHJvY2Vzc2VkLmRhdGEkbG9nMi5jcG0uZmlsdGVyZWQubm9ybQpteURHRUxpc3QuZmlsdGVyZWQubm9ybSA8LVNzUk5Bc2VxLnByZXByb2Nlc3NlZC5kYXRhJG15REdFTGlzdC5maWx0ZXJlZC5ub3JtCgpybShTc1JOQXNlcS5wcmVwcm9jZXNzZWQuZGF0YSkKCmxvYWQoZmlsZSA9ICIuLi9TdHJvbmd5bG9pZGVzX1JOQXNlcV9Ccm93c2VyL0RhdGEvU3NfdkRFR0xpc3QiKQoKYGBgCgoKIyBIaWVyYXJjaGljYWwgQ2x1c3RlcmluZyBhbmQgUHJpbmNpcGxlIENvbXBvbmVudHMgQW5hbHlzaXMgLS0tCmBgYHtyIG11bHRpdmFyaWF0ZX0KIyBJbnRyb2R1Y3Rpb24gdG8gdGhpcyBjaHVuayAtLS0tLS0tLS0tLQojIFRoaXMgY29kZSBjaHVuayBzdGFydHMgd2l0aCBmaWx0ZXJlZCBhbmQgbm9ybWFsaXplZCBhYnVuZGFuY2UgZGF0YSBpbiBhIGRhdGEgZnJhbWUgKG5vdCB0aWR5KS4KIyBJdCB3aWxsIGltcGxlbWVudCBoaWVyYXJjaGljYWwgY2x1c3RlcmluZyBhbmQgUENBIGFuYWx5c2VzIG9uIHRoZSBkYXRhLgojIEl0IHdpbGwgcGxvdCB2YXJpb3VzIGdyYXBocyBhbmQgY2FuIHNhdmUgdGhlbSBpbiBQREYgZmlsZXMuCiMgTG9hZCBwYWNrYWdlcyAtLS0tLS0Kc3VwcHJlc3NQYWNrYWdlU3RhcnR1cE1lc3NhZ2VzKHsKICBsaWJyYXJ5KHRpZHl2ZXJzZSkgIyB5b3UncmUgZmFtaWxpYXIgd2l0aCB0aGlzIGZyb210IHRoZSBwYXN0IHR3byBsZWN0dXJlcwogIGxpYnJhcnkoZ2dwbG90MikKICBsaWJyYXJ5KFJDb2xvckJyZXdlcikKICBsaWJyYXJ5KGdnZGVuZHJvKQogIGxpYnJhcnkobWFncml0dHIpCiAgbGlicmFyeShmYWN0b2V4dHJhKQogIGxpYnJhcnkoZ3JpZEV4dHJhKQogIGxpYnJhcnkoY293cGxvdCkKICBsaWJyYXJ5KGRlbmRleHRlbmQpCn0pCgojIElkZW50aWZ5IHZhcmlhYmxlcyBvZiBpbnRlcmVzdCBpbiBzdHVkeSBkZXNpZ24gZmlsZSAtLS0tCmdyb3VwIDwtIGZhY3Rvcih0YXJnZXRzJGdyb3VwKQoKIyBIaWVyYXJjaGljYWwgY2x1c3RlcmluZyAtLS0tLS0tLS0tLS0tLS0KIyBSZW1lbWJlcjogaGllcmFyY2hpY2FsIGNsdXN0ZXJpbmcgY2FuIG9ubHkgd29yayBvbiBhIGRhdGEgbWF0cml4LCBub3QgYSBkYXRhIGZyYW1lCgojIENhbGN1bGF0ZSBkaXN0YW5jZSBtYXRyaXgKIyBkaXN0IGNhbGN1bGF0ZXMgZGlzdGFuY2UgYmV0d2VlbiByb3dzLCBzbyB0cmFuc3Bvc2UgZGF0YSBzbyB0aGF0IHdlIGdldCBkaXN0YW5jZSBiZXR3ZWVuIHNhbXBsZXMuCiMgaG93IHNpbWlsYXIgYXJlIHNhbXBsZXMgZnJvbSBlYWNoIG90aGVyCmNvbG5hbWVzKGxvZzIuY3BtLmZpbHRlcmVkLm5vcm0pPC10YXJnZXRzJGdyb3VwCmRpc3RhbmNlIDwtIGRpc3QodChsb2cyLmNwbS5maWx0ZXJlZC5ub3JtKSwgbWV0aG9kID0gIm1heGltdW0iKSAjb3RoZXIgZGlzdGFuY2UgbWV0aG9kcyBhcmUgImV1Y2xpZGVhbiIsIG1heGltdW0iLCAibWFuaGF0dGFuIiwgImNhbmJlcnJhIiwgImJpbmFyeSIgb3IgIm1pbmtvd3NraSIKCiMgQ2FsY3VsYXRlIGNsdXN0ZXJzIHRvIHZpc3VhbGl6ZSBkaWZmZXJlbmNlcy4gVGhpcyBpcyB0aGUgaGllcmFyY2hpY2FsIGNsdXN0ZXJpbmcuCiMgVGhlIG1ldGhvZHMgaGVyZSBpbmNsdWRlOiBzaW5nbGUgKGkuZS4gImZyaWVuZHMtb2YtZnJpZW5kcyIpLCBjb21wbGV0ZSAoaS5lLiBjb21wbGV0ZSBsaW5rYWdlKSwgYW5kIGF2ZXJhZ2UgKGkuZS4gVVBHTUEpLiBIZXJlJ3MgYSBjb21wYXJpc29uIG9mIGRpZmZlcmVudCB0eXBlczogaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvVVBHTUEjQ29tcGFyaXNvbl93aXRoX290aGVyX2xpbmthZ2VzCmNsdXN0ZXJzIDwtIGhjbHVzdChkaXN0YW5jZSwgbWV0aG9kID0gImNvbXBsZXRlIikgI290aGVyIGFnZ2xvbWVyYXRpb24gbWV0aG9kcyBhcmUgIndhcmQuRCIsICJ3YXJkLkQyIiwgInNpbmdsZSIsICJjb21wbGV0ZSIsICJhdmVyYWdlIiwgIm1jcXVpdHR5IiwgIm1lZGlhbiIsIG9yICJjZW50cm9pZCIKZGVuZCA8LSBhcy5kZW5kcm9ncmFtKGNsdXN0ZXJzKSAKCnAxPC1kZW5kICU+JSAKICBkZW5kZXh0ZW5kOjpzZXQoImJyYW5jaGVzX2tfY29sb3IiLCBrID0gNSkgJT4lIAogIGRlbmRleHRlbmQ6OnNldCgiaGFuZ19sZWF2ZXMiLCBjKDAuMSkpICU+JSAKICBkZW5kZXh0ZW5kOjpzZXQoImxhYmVsc19jZXgiLCBjKDAuNSkpICU+JQogIGRlbmRleHRlbmQ6OnNldCgibGFiZWxzX2NvbG9ycyIsIGsgPSA1KSAlPiUgCiAgZGVuZGV4dGVuZDo6c2V0KCJicmFuY2hlc19sd2QiLCBjKDAuNykpICU+JSAKICAKICBhcy5nZ2RlbmQgJT4lCiAgZ2dwbG90IChvZmZzZXRfbGFiZWxzID0gLTAuNSkgKwogIHRoZW1lX2RlbmRybygpICsKICB5bGltKDAsIG1heChnZXRfYnJhbmNoZXNfaGVpZ2h0cyhkZW5kKSkpICsKICBsYWJzKHRpdGxlID0gIkhpZXJhcmNoaWNhbCBDbHVzdGVyIERlbmRyb2dyYW0gIiwKICAgICAgIHN1YnRpdGxlID0gImZpbHRlcmVkLCBUTU0gbm9ybWFsaXplZCIsCiAgICAgICB5ID0gIkRpc3RhbmNlIiwKICAgICAgIHggPSAiTGlmZSBzdGFnZSIpICsKICBjb29yZF9maXhlZCgxLzIpICsKICB0aGVtZShheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoY29sb3IgPSAiYmxhY2siKSwKICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCksCiAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoYW5nbGUgPSAwKSwKICAgICAgICBheGlzLmxpbmUueSA9IGVsZW1lbnRfbGluZShjb2xvciA9ICJibGFjayIpLAogICAgICAgIGF4aXMudGlja3MueSA9IGVsZW1lbnRfbGluZShjb2xvciA9ICJibGFjayIpLAogICAgICAgIGF4aXMudGlja3MubGVuZ3RoLnkgPSB1bml0KDIsICJtbSIpKQpwMQoKIyBQcmluY2lwYWwgY29tcG9uZW50IGFuYWx5c2lzIChQQ0EpIC0tLS0tLS0tLS0tLS0KIyB0aGlzIGFsc28gd29ya3Mgb24gYSBkYXRhIG1hdHJpeCwgbm90IGEgZGF0YSBmcmFtZQpwY2EucmVzIDwtIHByY29tcCh0KGxvZzIuY3BtLmZpbHRlcmVkLm5vcm0pLCBzY2FsZS49RiwgcmV0eD1UKQpzdW1tYXJ5KHBjYS5yZXMpICMgUHJpbnRzIHZhcmlhbmNlIHN1bW1hcnkgZm9yIGFsbCBwcmluY2lwYWwgY29tcG9uZW50cy4KCiNwY2EucmVzJHJvdGF0aW9uICMkcm90YXRpb24gc2hvd3MgeW91IGhvdyBtdWNoIGVhY2ggZ2VuZSBpbmZsdWVuY2VkIGVhY2ggUEMgKGNhbGxlZCAnc2NvcmVzJykKI3BjYS5yZXMkeCAjICd4JyBzaG93cyB5b3UgaG93IG11Y2ggZWFjaCBzYW1wbGUgaW5mbHVlbmNlZCBlYWNoIFBDIChjYWxsZWQgJ2xvYWRpbmdzJykKI25vdGUgdGhhdCB0aGVzZSBoYXZlIGEgbWFnbml0dWRlIGFuZCBhIGRpcmVjdGlvbiAodGhpcyBpcyB0aGUgYmFzaXMgZm9yIG1ha2luZyBhIFBDQSBwbG90KQojIyBUaGlzIGdlbmVyYXRlcyBhIHNjcmVlcGxvdDogYSBzdGFuZGFyZCB3YXkgdG8gdmlldyBlaWdlbnZhbHVlcyBmb3IgZWFjaCBQQ0EuIFNob3dzIHRoZSBwcm9wb3J0aW9uIG9mIHZhcmlhbmNlIGFjY291bnRlZCBmb3IgYnkgZWFjaCBQQy4gUGxvdHRpbmcgb25seSB0aGUgZmlyc3QgMTAgZGltZW5zaW9ucy4KcDI8LWZ2aXpfZWlnKHBjYS5yZXMsCiAgICAgICAgICAgICBiYXJjb2xvciA9IGJyZXdlci5wYWwoOCwiUGFzdGVsMiIpWzhdLAogICAgICAgICAgICAgYmFyZmlsbCA9IGJyZXdlci5wYWwoOCwiUGFzdGVsMiIpWzhdLAogICAgICAgICAgICAgbGluZWNvbG9yID0gImJsYWNrIiwKICAgICAgICAgICAgIG1haW4gPSAiU2NyZWUgcGxvdDogcHJvcG9ydGlvbiBvZiB2YXJpYW5jZSBhY2NvdW50ZWQgZm9yIGJ5IGVhY2ggcHJpbmNpcGFsIGNvbXBvbmVudCIsCiAgICAgICAgICAgICBnZ3RoZW1lID0gdGhlbWVfYncoKSkgCnAyCgpwYy52YXI8LXBjYS5yZXMkc2Rldl4yICMgc2Rldl4yIGNhcHR1cmVzIHRoZXNlIGVpZ2VudmFsdWVzIGZyb20gdGhlIFBDQSByZXN1bHQKcGMucGVyPC1yb3VuZChwYy52YXIvc3VtKHBjLnZhcikqMTAwLCAxKSAjIHdlIGNhbiB0aGVuIHVzZSB0aGVzZSBlaWdlbnZhbHVlcyB0byBjYWxjdWxhdGUgdGhlIHBlcmNlbnRhZ2UgdmFyaWFuY2UgZXhwbGFpbmVkIGJ5IGVhY2ggUEMKCiMgVmlzdWFsaXplIHRoZSBQQ0EgcmVzdWx0IC0tLS0tLS0tLS0tLS0tLS0tLQojbGV0cyBmaXJzdCBwbG90IGFueSB0d28gUENzIGFnYWluc3QgZWFjaCBvdGhlcgojV2Uga25vdyBob3cgbXVjaCBlYWNoIHNhbXBsZSBjb250cmlidXRlcyB0byBlYWNoIFBDIChsb2FkaW5ncyksIHNvIGxldCdzIHBsb3QKcGNhLnJlcy5kZiA8LSBhc190aWJibGUocGNhLnJlcyR4KQoKIyBQbG90dGluZyBQQzEgYW5kIFBDMgpwMzwtZ2dwbG90KHBjYS5yZXMuZGYpICsKICBhZXMoeD1QQzEsIHk9UEMyLCBsYWJlbD10YXJnZXRzJGdyb3VwLCAKICAgICAgZmlsbCA9IHRhcmdldHMkZ3JvdXAsCiAgICAgIGNvbG9yID0gdGFyZ2V0cyRncm91cAogICkgKwogIGdlb21fcG9pbnQoc2l6ZT00LCBzaGFwZT0gMjEsIGNvbG9yID0gImJsYWNrIiwgYWxwaGEgPSAwLjUpICsKICAjZ2VvbV9sYWJlbChjb2xvciA9ICJibGFjayIsIHNpemUgPSAyKSArCiAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICJTZXQyIikgKwogIHNjYWxlX2NvbG9yX2JyZXdlcihwYWxldHRlID0gIlNldDIiLCBndWlkZSA9IEZBTFNFKSArCiAgI3N0YXRfZWxsaXBzZSgpICsKICB4bGFiKHBhc3RlMCgiUEMxICgiLHBjLnBlclsxXSwiJSIsIikiKSkgKyAKICB5bGFiKHBhc3RlMCgiUEMyICgiLHBjLnBlclsyXSwiJSIsIikiKSkgKwogIGxhYnModGl0bGU9IlByaW5jaXBhbCBDb21wb25lbnRzIEFuYWx5c2lzIG9mIFMuIHN0ZXJjb3JhbGlzIFJOQXNlcSBTYW1wbGVzIiwKICAgICAgIHN1YiA9ICJOb3RlOiBhbmFseXNpcyBpcyBibGluZCB0byBsaWZlIHN0YWdlIGlkZW50aXR5LiIsCiAgICAgICBmaWxsID0gIkxpZmUgU3RhZ2UiKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGV4cGFuZCA9IGMoLjMsIC4zKSkgKwogIHNjYWxlX3lfY29udGludW91cyhleHBhbmQgPSBjKC4zLCAuMykpICsKICBjb29yZF9maXhlZCgpICsKICB0aGVtZV9idygpCnAzCgojIEEgZ3Vlc3MgYXQgdGhlIGlkZW50aXR5IG9mIFBDMQpwNDwtZ2dwbG90KHBjYS5yZXMuZGYpICsKICBhZXMoeD1QQzEsIHk9UEMyLCBsYWJlbD10YXJnZXRzJGdyb3VwLAogICAgICBjb2xvciA9IGZhY3Rvcih0YXJnZXRzJE1hdHVyaXR5KSwKICAgICAgZmlsbCA9IGZhY3Rvcih0YXJnZXRzJE1hdHVyaXR5KQogICkgKwogICNnZW9tX3BvaW50KHNpemU9NCkgKwogICNzdGF0X2VsbGlwc2UoKSArCiAgZ2VvbV9sYWJlbChjb2xvciA9ICJibGFjayIsIHNpemUgPSAyLCBhbHBoYSA9IDAuNykgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGJyZXdlci5wYWwoOCwiU2V0MiIpKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGJyZXdlci5wYWwoOCwiU2V0MiIpLCBndWlkZSA9IEZBTFNFKSArCiAgeGxhYihwYXN0ZTAoIlBDMSAoIixwYy5wZXJbMV0sIiUiLCIpIikpICsgCiAgeWxhYihwYXN0ZTAoIlBDMiAoIixwYy5wZXJbMl0sIiUiLCIpIikpICsKICBsYWJzKHN1YnRpdGxlPSJQb3RlbnRpYWwgUEMxIGlkZW50aXR5OiBBZHVsdHMgdnMgTGFydmFlIiwKICAgICAgIGZpbGwgPSAiTWF0dXJpdHkiKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGV4cGFuZCA9IGMoLjMsIC4zKSkgKwogIHNjYWxlX3lfY29udGludW91cyhleHBhbmQgPSBjKC4zLCAuMykpICsKICBjb29yZF9maXhlZCgpICsKICB0aGVtZV9idygpCnA0CgojIEEgZ3Vlc3MgYXQgdGhlIGlkZW50aXR5IG9mIFBDMgpwNTwtZ2dwbG90KHBjYS5yZXMuZGYpICsKICBhZXMoeD1QQzEsIHk9UEMyLCBsYWJlbD10YXJnZXRzJGdyb3VwLCAKICAgICAgY29sb3IgPSBmYWN0b3IodGFyZ2V0cyRJbmZlY3Rpb3VzKSwKICAgICAgZmlsbCA9IGZhY3Rvcih0YXJnZXRzJEluZmVjdGlvdXMpCiAgKSArCiAgI2dlb21fcG9pbnQoc2l6ZT00KSArCiAgI3N0YXRfZWxsaXBzZSgpICsKICBnZW9tX2xhYmVsKGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IDIsIGFscGhhID0gMC43KSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYnJld2VyLnBhbCg4LCJTZXQyIilbMzo0XSkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBicmV3ZXIucGFsKDgsIlNldDIiKVszOjRdLCBndWlkZSA9IEZBTFNFKSArCiAgeGxhYihwYXN0ZTAoIlBDMSAoIixwYy5wZXJbMV0sIiUiLCIpIikpICsgCiAgeWxhYihwYXN0ZTAoIlBDMiAoIixwYy5wZXJbMl0sIiUiLCIpIikpICsKICBsYWJzKHN1YnRpdGxlPSJQb3RlbnRpYWwgUEMyIGlkZW50aXR5OiBIb3N0LXNlZWtpbmcvZHdlbGxpbmciLAogICAgICAgZmlsbCA9ICdJbmZlY3Rpdml0eSBTdGFnZScpICsKICBzY2FsZV94X2NvbnRpbnVvdXMoZXhwYW5kID0gYyguMywgLjMpKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGV4cGFuZCA9IGMoLjMsIC4zKSkgKwogIGNvb3JkX2ZpeGVkKCkgKwogIHRoZW1lX2J3KCkKcDUKCiMgQ3JlYXRlIGEgUENBICdzbWFsbCBtdWx0aXBsZXMnIGNoYXJ0IC0tLS0KcGNhLnJlcy5kZiA8LSBwY2EucmVzJHhbLDE6Nl0gJT4lIAogIGFzX3RpYmJsZSgpICU+JQogIGFkZF9jb2x1bW4oc2FtcGxlID0gdGFyZ2V0cyRzYW1wbGUsCiAgICAgICAgICAgICBncm91cCA9IGdyb3VwLAogICAgICAgICAgICAgbWF0dXJpdHkgPSBmYWN0b3IodGFyZ2V0cyRNYXR1cml0eSksCiAgICAgICAgICAgICBpbmZlY3Rpb3VzID0gZmFjdG9yKHRhcmdldHMkSW5mZWN0aW91cykpCgpwY2EucGl2b3QgPC0gcGl2b3RfbG9uZ2VyKHBjYS5yZXMuZGYsICMgZGF0YWZyYW1lIHRvIGJlIHBpdm90ZWQKICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xzID0gUEMxOlBDNiwgIyBjb2x1bW4gbmFtZXMgdG8gYmUgc3RvcmVkIGFzIGEgU0lOR0xFIHZhcmlhYmxlCiAgICAgICAgICAgICAgICAgICAgICAgICAgbmFtZXNfdG8gPSAiUEMiLCAjIG5hbWUgb2YgdGhhdCBuZXcgdmFyaWFibGUgKGNvbHVtbikKICAgICAgICAgICAgICAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAibG9hZGluZ3MiKSAjIG5hbWUgb2YgbmV3IHZhcmlhYmxlIChjb2x1bW4pIHN0b3JpbmcgYWxsIHRoZSB2YWx1ZXMgKGRhdGEpClBDMTwtc3Vic2V0KHBjYS5waXZvdCwgUEMgPT0gIlBDMSIpClBDMiA8LXN1YnNldChwY2EucGl2b3QsIFBDID09ICJQQzIiKQojUEMzIDwtIHN1YnNldChwY2EucGl2b3QsIFBDID09ICJQQzMiKQojUEM0IDwtIHN1YnNldChwY2EucGl2b3QsIFBDID09ICJQQzQiKQoKcDY8LWdncGxvdChwY2EucGl2b3QpICsKICBhZXMoeD1zYW1wbGUsIHk9bG9hZGluZ3MpICsgIyB5b3UgY291bGQgaXRlcmF0aXZlbHkgJ3BhaW50JyBkaWZmZXJlbnQgY292YXJpYXRlcyBvbnRvIHRoaXMgcGxvdCB1c2luZyB0aGUgJ2ZpbGwnIGFlcwogIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5IiwgZmlsbCA9IGJyZXdlci5wYWwoOCwiUGFzdGVsMiIpWzhdKSArCiAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICJTZXQyIikgKwogIGZhY2V0X3dyYXAoflBDKSArCiAgZ2VvbV9iYXIoZGF0YSA9IFBDMSwgc3RhdCA9ICJpZGVudGl0eSIsIGFlcyhmaWxsID0gbWF0dXJpdHkpKSArCiAgZ2VvbV9iYXIoZGF0YSA9IFBDMiwgc3RhdCA9ICJpZGVudGl0eSIsIGFlcyhmaWxsID0gaW5mZWN0aW91cykpICsKICBsYWJzKHRpdGxlPSJQQ0EgJ3NtYWxsIG11bHRpcGxlcycgcGxvdCIsCiAgICAgICBmaWxsID0gIkxpZmUgU3RhZ2UgR3JvdXBzIiwKICAgICAgIGNhcHRpb249cGFzdGUwKCJwcm9kdWNlZCBvbiAiLCBTeXMudGltZSgpKSkgKwogIHNjYWxlX3hfZGlzY3JldGUobGltaXRzID0gdGFyZ2V0cyRzYW1wbGUsIGxhYmVscyA9IHRhcmdldHMkZ3JvdXApICsKICB0aGVtZV9idygpICsKICBjb29yZF9mbGlwKCkKcDYKCiMgR3JhcGggYWxsIHRoZSBwbG90cyBnZW5lcmF0ZWQgaW4gdGhpcyBzY3JpcHQgLS0tLQojIFBsb3QgYWxsIHRoZSBwbG90cyBvbiBhIGdyaWQKIyBwNzwtIGdyaWQuYXJyYW5nZShwMSxwMixwMyxwNCwgcDUscDYsbmNvbCA9IDQsCiMgICAgICAgICAgICAgbGF5b3V0X21hdHJpeCA9IGNiaW5kKGMoMSwxLDEsMiwyLDIpLGMoMywzLDQsNCw1LDUpLGMoNiw2LDYsNiw2LDYpLCBjKDYsNiw2LDYsNiw2KSkpCiMgZ2dzYXZlKCJTdHJvbmd5bG9pZGVzIHN0ZXJjb3JhbGlzIFJOQXNlcSBNdWx0aXZhcmlhdGUgQW5hbHlzaXMucGRmIiwgCiMgICAgICAgIHBsb3QgPSBwNywgCiMgICAgICAgIGRldmljZSA9ICJwZGYiLAojICAgICAgICAjaGVpZ2h0ID0gMTcsCiMgICAgICAgIHdpZHRoID0gMjIsCiMgICAgICAgIHBhdGggPSAnLi4vT3V0cHV0cy8nCgpgYGAKCiMgR2VuZXMgQ29udHJpYnV0aW5nIHRvIFBDIElkZW50aXR5IC0tLQpgYGB7ciBpZFBDZ2VuZXN9CiMgSW50cm9kdWN0aW9uIHRvIHRoaXMgY2h1bmsgLS0tLQojIFRoaXMgY2h1bmsgcHJvdmlkZXMgYWRkaXRpb25hbCBhbmFseXNpcyBvZiB0aGUgcHJpbmNpcGFsIGNvbXBvbmVudHMsIGluIG9yZGVyIHRvIGRldGVybWluZSB3aGljaCBnZW5lcyBhcmUgaW5mbHVlbmNpbmcgdGhlIGlkZW50aWZpZWQgUENzLgoKIyBVc2UgcGNhLnJlcyRyb3RhdGlvbiB0byBzZWxlY3QgZ2VuZXMgaW5mbHVlbmNpbmcgUEMxLTYgLS0tLQpteXNjb3Jlcy5kZiA8LSBwY2EucmVzJHJvdGF0aW9uWywxOjZdICU+JSAKICBhc190aWJibGUocm93bmFtZXMgPSAiZ2VuZUlEIikgJT4lCiAgcGl2b3RfbG9uZ2VyKGNvbHMgPSAtZ2VuZUlELCBuYW1lc190byA9ICJQQyIsIHZhbHVlc190byA9ICJzY29yZXMiKSAlPiUKICBkcGx5cjo6bXV0YXRlKGFic19zY29yZXMgPSBhYnMoc2NvcmVzKSkgJT4lCiAgZ3JvdXBfYnkoUEMpICU+JQogIHNsaWNlX21heChhYnNfc2NvcmVzLCBwcm9wID0gLjEpICMgZ2V0IHRvcCAxMCUgb2YgZ2VuZXMgaW4gYWxsIFBDcwoKIyBQdWxsIG91dCBnZW5lcyB0aGF0IGFyZSB0aGUgdG9wIDEwJSBvZiBjb250cmlidXRvcnMgKGluIGFueSBkaXJlY3Rpb24pIHRvIFBDMSBhbmQgUEMyLiBBbm5vdGF0ZS4KbXlzY29yZXMuVG9wMTAgPC0gbXlzY29yZXMuZGYgJT4lCiAgZHBseXI6OmZpbHRlcihQQyA9PSAiUEMxIiB8IFBDID09ICJQQzIiKSAlPiUKICBwaXZvdF93aWRlcihpZF9jb2xzID0gZ2VuZUlELAogICAgICAgICAgICAgIG5hbWVzX2Zyb20gPSBQQywKICAgICAgICAgICAgICB2YWx1ZXNfZnJvbSA9IHNjb3JlcykgJT4lCiAgZHBseXI6Om11dGF0ZShQQzFfaWQgPSBjYXNlX3doZW4oUEMxID4gMCB+ICJsYXJ2YWwiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFBDMSA8IDAgfiAiYWR1bHQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlzLm5hKFBDMSkgfiAiLS0iKSkgJT4lICAKICBkcGx5cjo6bXV0YXRlKFBDMl9pZCA9IGNhc2Vfd2hlbihQQzIgPiAwIH4gInBhcmFzaXRlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBQQzIgPCAwIH4gIm5vbl9wYXJhc2l0ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaXMubmEoUEMyKSB+ICItLSIpKSAlPiUgIAogIGRwbHlyOjpsZWZ0X2pvaW4oLiwocm93bmFtZXNfdG9fY29sdW1uKGFubm90YXRpb25zLCB2YXIgPSAiZ2VuZUlEIikpLCBieSA9ICJnZW5lSUQiKSAlPiUKICBkcGx5cjo6cmVsb2NhdGUoVW5pUHJvdEtCLCBEZXNjcmlwdGlvbiwgSW50ZXJQcm8sIEdPX3Rlcm0sIENlX2dlbmVJRCwgQ2VfcGVyY2VudF9ob21vbG9neSwgLmFmdGVyID0gUEMyX2lkKQoKIyBNYWtlIEludGVyYWN0aXZlIFBsb3QKbXlzY29yZXMuVG9wMTAuaW50ZXJhY3RpdmUgPC0gbXlzY29yZXMuVG9wMTAgJT4lCiAgRFQ6OmRhdGF0YWJsZShleHRlbnNpb25zID0gYygnS2V5VGFibGUnLCAiRml4ZWRIZWFkZXIiKSwKICAgICAgICAgICAgICAgIHJvd25hbWVzID0gRkFMU0UsCiAgICAgICAgICAgICAgICBjYXB0aW9uID0gaHRtbHRvb2xzOjp0YWdzJGNhcHRpb24oCiAgICAgICAgICAgICAgICAgIHN0eWxlID0gJ2NhcHRpb24tc2lkZTogdG9wOyB0ZXh0LWFsaWduOiBsZWZ0OycsCiAgICAgICAgICAgICAgICAgIGh0bWx0b29sczo6dGFncyRiKCdUb3AgMTAlIG9mIEdlbmVzIENvbnRyaWJ1dGluZyB0byBQQzEgYW5kIFBDMicpLAogICAgICAgICAgICAgICAgICBodG1sdG9vbHM6OnRhZ3MkYnIoKSwKICAgICAgICAgICAgICAgICAgJ1NlYXJjaCBmb3IgUEMxX2lkL1BDMl9pZCB2YWx1ZXMgdG8gZmlsdGVyIHJlc3VsdHMnKSwKICAgICAgICAgICAgICAgIG9wdGlvbnMgPSBsaXN0KGtleXMgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXV0b1dpZHRoID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNjcm9sbFggPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb3JkZXIgPSBsaXN0KDEsICdkZXNjJyksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZWFyY2hIaWdobGlnaHQgPSBUUlVFLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhZ2VMZW5ndGggPSAxMCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZW5ndGhNZW51ID0gYygiMTAiLCAiMjUiLCAiNTAiLCAiMTAwIikpKSAlPiUKICBEVDo6Zm9ybWF0Um91bmQoY29sdW1ucz1jKDI6MyksIGRpZ2l0cz0zKQoKbXlzY29yZXMuVG9wMTAuaW50ZXJhY3RpdmUKYGBgCiMgSGVhdG1hcCBvZiBHZW5lIEV4cHJlc3Npb24gQWNyb3NzIExpZmUgU3RhZ2VzIC0tLQpgYGB7ciBnZW5lLmV4cHJlc3Npb24uaGVhdG1hcH0KbGlicmFyeShwaGVhdG1hcCkKbGlicmFyeShSQ29sb3JCcmV3ZXIpCiMgTWFrZSBhIGhlYXRtYXAgZm9yIGFsbCB0aGUgZ2VuZXMgdXNpbmcgdGhlIExvZzJDUE0gdmFsdWVzCgpkaWZmR2VuZXMgPC0gdi5ERUdMaXN0LmZpbHRlcmVkLm5vcm0kRSAlPiUKICBhc190aWJibGUocm93bmFtZXMgPSAiZ2VuZUlEIiwgLm5hbWVfcmVwYWlyID0gInVuaXF1ZSIpICU+JQogIGRwbHlyOjpzZWxlY3QoIWdlbmVJRCkgJT4lCiAgYXMubWF0cml4KCkKcm93bmFtZXMoZGlmZkdlbmVzKSA8LSByb3duYW1lcyh2LkRFR0xpc3QuZmlsdGVyZWQubm9ybSRFKQpjb2xuYW1lcyhkaWZmR2VuZXMpIDwtIGFzLmNoYXJhY3Rlcih2LkRFR0xpc3QuZmlsdGVyZWQubm9ybSR0YXJnZXRzJGdyb3VwKQpjbHVzdENvbHVtbnMgPC0gaGNsdXN0KGFzLmRpc3QoMS1jb3IoZGlmZkdlbmVzLCBtZXRob2Q9InNwZWFybWFuIikpLCBtZXRob2Q9ImNvbXBsZXRlIikKY2x1c3RSb3dzIDwtIGhjbHVzdChhcy5kaXN0KDEtY29yKHQoZGlmZkdlbmVzKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtZXRob2Q9InBlYXJzb24iKSksIAogICAgICAgICAgICAgICAgICAgIG1ldGhvZD0iY29tcGxldGUiKSAKcGFyKGNleC5tYWluPTEuMikKCnA8LXBoZWF0bWFwKGRpZmZHZW5lcywKICAgICAgICAgICAgY29sb3IgPSBSZEJ1KDc1KSwKICAgICAgICAgICAgY2x1c3Rlcl9yb3dzID0gY2x1c3RSb3dzLAogICAgICAgICAgICBjbHVzdGVyX2NvbHMgPSBjbHVzdENvbHVtbnMsCiAgICAgICAgICAgIHNob3dfcm93bmFtZXMgPSBGLAogICAgICAgICAgICBzY2FsZSA9ICJyb3ciLAogICAgICAgICAgICBhbmdsZV9jb2wgPSA0NSwKICAgICAgICAgICAgbWFpbiA9ICJMb2cyIENvdW50cyBQZXIgTWlsbGlvbiAoQ1BNKSBFeHByZXNzaW9uIEFjcm9zcyBMaWZlIFN0YWdlcyIKICAgICAgICAgIAopCgpgYGAKCiMgRGlmZmVyZW50aWFsbHkgRXhwcmVzc2VkIEdlbmVzIC0tLQpgYGB7ciBERUcsIGVjaG89VFJVRSwgd2FybmluZz1GQUxTRSwgcGFnZWQucHJpbnQ9VFJVRX0KIyBJbnRyb2R1Y3Rpb24gdG8gdGhpcyBjaHVuayAtLS0tCiMgVGhpcyBjaHVuayB1c2VzIGEgdmFyaWFuY2Utc3RhYmlsaXplZCBER0VMaXN0IG9mIGZpbHRlcmVkIGFuZCBub3JtYWxpemVkIGFidW5kYW5jZSBkYXRhLgojIAojIFRoZXNlIGRhdGEvcmVzdWx0cyBhcmUgZXhhbXBsZXMsIGEgcmVzcG9uc2l2ZSB2ZXJzaW9uIG9mIHRoaXMgY29kZSBpcyBhdmFsaWFibGUgaW4gYSBTaGlueSBBcHAuCiMgCiMgQmVjYXVzZSB3ZSBoYXZlIGFjY2VzcyB0byBiaW9sb2dpY2FsIHJlcGxpY2F0ZXMsIHdlIGNhbiB1c2Ugc3RhdGlzdGljYWwgdG9vbHMgZm9yIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIGFuYWx5c2lzCiMgVXNlZnVsIHJlYWRpbmcgb24gZGlmZmVyZW50aWFsIGV4cHJlc3Npb246IGh0dHBzOi8vdWNkYXZpcy1iaW9pbmZvcm1hdGljcy10cmFpbmluZy5naXRodWIuaW8vMjAxOC1KdW5lLVJOQS1TZXEtV29ya3Nob3AvdGh1cnNkYXkvREUuaHRtbAoKIyBMb2FkIHBhY2thZ2VzIC0tLS0Kc3VwcHJlc3NQYWNrYWdlU3RhcnR1cE1lc3NhZ2VzKHsKICBsaWJyYXJ5KHRpZHl2ZXJzZSkKICBsaWJyYXJ5KGxpbW1hKSAjIGRpZmZlcmVudGlhbCBnZW5lIGV4cHJlc3Npb24gdXNpbmcgbGluZWFyIG1vZGVsaW5nCiAgbGlicmFyeShlZGdlUikKICBsaWJyYXJ5KGd0KSAKICBsaWJyYXJ5KERUKSAKICBsaWJyYXJ5KHBsb3RseSkKICBsaWJyYXJ5KGdndGhlbWVzKQogIGxpYnJhcnkoUkNvbG9yQnJld2VyKQogIHNvdXJjZSgiLi4vU3Ryb25neWxvaWRlc19STkFzZXFfQnJvd3Nlci9TZXJ2ZXIvdGhlbWVfUHVibGljYXRpb24uUiIpCn0pCgpkaWZmR2VuZXMuZGYgPC0gdi5ERUdMaXN0LmZpbHRlcmVkLm5vcm0kRSAlPiUKICBhc190aWJibGUocm93bmFtZXMgPSAiZ2VuZUlEIiwgLm5hbWVfcmVwYWlyID0gInVuaXF1ZSIpCgojIFNldCBFeHByZXNzaW9uIHRocmVzaG9sZCB2YWx1ZXMgZm9yIHBsb3R0aW5nIGFuZCBzYXZpbmcgREVHcyAtLS0tCmFkai5QLnRocmVzaCA8LSAwLjA1CmxmYy50aHJlc2ggPC0gMSAKCmdyb3VwIDwtIGZhY3Rvcih0YXJnZXRzJGdyb3VwKQpkZXNpZ24gPC0gbW9kZWwubWF0cml4KH4wICsgZ3JvdXApICMgbm8gaW50ZXJjZXB0L2Jsb2NraW5nIGZvciBtYXRyaXgsIGNvbXBhcmlzb25zIGFjcm9zcyBncm91cApjb2xuYW1lcyhkZXNpZ24pIDwtIGxldmVscyhncm91cCkKCiMgRml0IGEgbGluZWFyIG1vZGVsIHRvIHRoZSBkYXRhIC0tLS0KZml0IDwtIGxtRml0KHYuREVHTGlzdC5maWx0ZXJlZC5ub3JtLCBkZXNpZ24pCgojIEFzIGFuIGV4YW1wbGUsIGdlbmVyYXRlIGNvbXBhcmlzb24gbWF0cml4IGZvciBhIHBhaXJ3aXNlIGNvbXBhcmlzb24gLS0tLQojIGlMM3MgdnMgRkxGCiMgTm90ZSB0aGF0IHRoZSB0YXJnZXQvY29udHJhc3QgZ291cHMgd2lsbCBiZSBkaXZpZGVkIGJ5IHRoZSBudW1iZXIgb2YgbGlmZSAKIyBzdGFnZSBncm91cHMgZS5nLiBQRitGTEYvMiAtIGlMMytpTDNhK3BmTDErcHBMMStwcEwzLzUKY29tcGFyaXNvbiA8LSBjKCcoaUwzKS0oRkxGKScpCgp0YXJnZXRTdGFnZTwtIGNvbXBhcmlzb24gJT4lCiAgc3RyX3NwbGl0KHBhdHRlcm49Ii0iLCBzaW1wbGlmeSA9IFQpICU+JQogIC5bLDFdICU+JQogIGdzdWIoIigiLCAiIiwgLiwgZml4ZWQgPSBUUlVFKSAlPiUKICBnc3ViKCIpIiwgIiIsIC4sIGZpeGVkID0gVFJVRSkgJT4lCiAgc3RyX3NwbGl0KHBhdHRlcm4gPSAiXFwrIiwgc2ltcGxpZnkgPSBUKQoKY29udHJhc3RTdGFnZTwtY29tcGFyaXNvbiAlPiUKICBzdHJfc3BsaXQocGF0dGVybj0iLSIsIHNpbXBsaWZ5ID0gVCkgJT4lCiAgLlssMl0gJT4lCiAgZ3N1YigiKCIsICIiLCAuLCBmaXhlZCA9IFRSVUUpICU+JQogIGdzdWIoIikiLCAiIiwgLiwgZml4ZWQgPSBUUlVFKSAgJT4lCiAgc3RyX3NwbGl0KHBhdHRlcm4gPSAiXFwrIiwgc2ltcGxpZnkgPSBUKQoKY29tcGFyaXNvbjwtIHNhcHBseShzZXFfYWxvbmcoY29tcGFyaXNvbiksZnVuY3Rpb24oeCl7CiAgdFMgPC0gYXMudmVjdG9yKHRhcmdldFN0YWdlW3gsXSkgJT4lCiAgICAuWy4gIT0gIiJdIAogIGNTIDwtIGFzLnZlY3Rvcihjb250cmFzdFN0YWdlW3gsXSkgJT4lCiAgICAuWy4gIT0gIiJdIAogIHBhc3RlKHBhc3RlMCh0UywgCiAgICAgICAgICAgICAgIGNvbGxhcHNlID0gIisiKSAlPiUKICAgICAgICAgIHBhc3RlMCgiKCIsLiwiKS8iLGxlbmd0aCh0UykpLAogICAgICAgIHBhc3RlMChjUywgCiAgICAgICAgICAgICAgIGNvbGxhcHNlID0gIisiKSAlPiUKICAgICAgICAgIHBhc3RlMCgiKCIsLiwiKS8iLGxlbmd0aChjUykpLAogICAgICAgIHNlcCA9ICItIikKICAKfSkKCiMgR2VuZXJhdGUgY29udHJhc3QgbWF0cml4IC0tLS0KY29udHJhc3QubWF0cml4IDwtIG1ha2VDb250cmFzdHMoY29udHJhc3RzID0gY29tcGFyaXNvbiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzPWRlc2lnbikKCiMgZXh0cmFjdCB0aGUgbGluZWFyIG1vZGVsIGZpdCAtLS0tLQpmaXRzIDwtIGNvbnRyYXN0cy5maXQoZml0LCBjb250cmFzdC5tYXRyaXgpCiMgZW1waXJpY2FsIGJheWVzIHNtb290aGluZyBvZiBnZW5lLXdpc2Ugc3RhbmRhcmQgZGV2aWF0aW9ucyBwcm92aWRlcyBpbmNyZWFzZWQgcG93ZXIgKHNlZTogaHR0cHM6Ly93d3cuZGVncnV5dGVyLmNvbS9kb2kvMTAuMjIwMi8xNTQ0LTYxMTUuMTAyNykKZWJGaXQgPC0gZUJheWVzKGZpdHMpCgojIFB1bGwgb3V0IHRoZSBERUdzIHRoYXQgcGFzcyBhIHNwZWNpZmljIHRocmVzaG9sZCBmb3IgYWxsIHBhaXJ3aXNlIGNvbXBhcmlzb25zIC0tLS0KIyBBZGp1c3QgZm9yIG11bHRpcGxlIGNvbXBhcmlzb25zIHVzaW5nIG1ldGhvZCA9IGdsb2JhbC4gCnJlc3VsdHMgPC0gZGVjaWRlVGVzdHMoZWJGaXQsIG1ldGhvZD0iZ2xvYmFsIiwgYWRqdXN0Lm1ldGhvZD0iQkgiLCBwLnZhbHVlID0gYWRqLlAudGhyZXNoKQoKcmVjb2RlMDE8LSBmdW5jdGlvbih4KXsKICBjYXNlX3doZW4oeCA9PSAxIH4gIlVwIiwKICAgICAgICAgICAgeCA9PSAtMSB+ICJEb3duIiwKICAgICAgICAgICAgeCA9PSAwIH4gIk5vdFNpZyIpCn0KZGlmZkRlc2MgPC0gcmVzdWx0cyAlPiUKICBhc190aWJibGUocm93bmFtZXMgPSAiZ2VuZUlEIikgJT4lCiAgZHBseXI6Om11dGF0ZShhY3Jvc3MoLWdlbmVJRCwgdW5jbGFzcykpICU+JQogIGRwbHlyOjptdXRhdGUoYWNyb3NzKHdoZXJlKGlzLmRvdWJsZSksIHJlY29kZTAxKSkKCiMgRnVuY3Rpb24gdGhhdCBpZGVudGlmaWVzIHRvcCBERUdzIGJldHdlZW4gYSBzcGVjaWZpYyBjb250cmFzdCAtLS0tCmNhbGNfREVHX3RibCA8LSBmdW5jdGlvbiAoZWJGaXQsIGNvZWYpIHsKICBteVRvcEhpdHMuZGYgPC0gbGltbWE6OnRvcFRhYmxlKGViRml0LCBhZGp1c3QgPSJCSCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29lZj1jb2VmLCBudW1iZXI9NDAwMDAsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc29ydC5ieT0ibG9nRkMiKSAlPiUKICAgIGFzX3RpYmJsZShyb3duYW1lcyA9ICJnZW5lSUQiKSAlPiUKICAgIGRwbHlyOjpyZW5hbWUodFN0YXRpc3RpYyA9IHQsIExvZ09kZHMgPSBCLCBCSC5hZGouUC5WYWwgPSBhZGouUC5WYWwpICU+JQogICAgZHBseXI6OnJlbG9jYXRlKFVuaVByb3RLQiwgRGVzY3JpcHRpb24sIEludGVyUHJvLCBHT190ZXJtLCAgU3RyX2dlbmVJRCwgU3RyX1dCZ2VuZUlELCBTdHJfcGVyY2VudF9ob21vbG9neSwgQ2VfZ2VuZUlELCBDZV9wZXJjZW50X2hvbW9sb2d5LCAuYWZ0ZXIgPSBMb2dPZGRzKQogIAogIG15VG9wSGl0cy5kZgp9CgpsaXN0Lm15VG9wSGl0cy5kZiA8LSBzYXBwbHkoY29tcGFyaXNvbiwgZnVuY3Rpb24oeSl7CiAgY2FsY19ERUdfdGJsKGViRml0LCB5KX0sIAogIHNpbXBsaWZ5ID0gRkFMU0UsIAogIFVTRS5OQU1FUyA9IFRSVUUpCgpsaXN0Lm15VG9wSGl0cy5kZiA8LSBzYXBwbHkoY29tcGFyaXNvbiwgZnVuY3Rpb24oeSl7CiAgbGlzdC5teVRvcEhpdHMuZGZbW3ldXSAlPiUKICAgIGRwbHlyOjpzZWxlY3QoZ2VuZUlELCAKICAgICAgICAgICAgICAgICAgbG9nRkMsIAogICAgICAgICAgICAgICAgICBCSC5hZGouUC5WYWw6Q2VfcGVyY2VudF9ob21vbG9neSl9LAogIHNpbXBsaWZ5ID0gRkFMU0UsIAogIFVTRS5OQU1FUyA9IFRSVUUpCgojIEdldCBsb2cyQ1BNIHZhbHVlcyBhbmQgdGhyZXNob2xkIGluZm9ybWF0aW9uIGZvciBnZW5lcyBvZiBpbnRlcmVzdApsaXN0Lm15VG9wSGl0cy5kZiA8LSBzYXBwbHkoc2VxX2Fsb25nKGNvbXBhcmlzb24pLCBmdW5jdGlvbih5KXsKICB0UzwtIHRhcmdldFN0YWdlW3ksXVt0YXJnZXRTdGFnZVt5LF0hPSIiXQogIGNTPC0gY29udHJhc3RTdGFnZVt5LF1bY29udHJhc3RTdGFnZVt5LF0hPSIiXQogIAogIGNvbmNhdF9uYW1lIDwtIGZ1bmN0aW9uKHgpIHsKICAgIGlmZWxzZSh4ID09ICJ0YXJnZXQiLCAKICAgICAgICAgICBwYXN0ZSh0UywgY29sbGFwc2UgPSAiKyIpLCAKICAgICAgICAgICBwYXN0ZShjUywgY29sbGFwc2UgPSAiKyIpKQogIH0KICAKICBncm91cEF2Z3MgPC0gZGlmZkdlbmVzLmRmICU+JQogICAgZHBseXI6OnNlbGVjdChnZW5lSUQsIHN0YXJ0c193aXRoKHBhc3RlMCh0UywiLSIpKSwgCiAgICAgICAgICAgICAgICAgIHN0YXJ0c193aXRoKHBhc3RlMChjUywiLSIpKSkgJT4lCiAgICBwaXZvdF9sb25nZXIoY29scyA9IC1nZW5lSUQsIG5hbWVzX3RvID0gYygiZ3JvdXAiLCJzYW1wbGUiKSwgdmFsdWVzX3RvID0gIkNQTSIsCiAgICAgICAgICAgICAgICAgbmFtZXNfc2VwID0gIi0iKSAlPiUKICAgIGRwbHlyOjptdXRhdGUoY29udHJhc3RJRCA9IGlmX2Vsc2UoZ3JvdXAgJWluJSB0UywidGFyZ2V0IiwgImNvbnRyYXN0IikpICU+JQogICAgZ3JvdXBfYnkoZ2VuZUlELCBjb250cmFzdElEKSAlPiUKICAgIGRwbHlyOjpzZWxlY3QoLXNhbXBsZSkgJT4lCiAgICBzdW1tYXJpemUobWVhbiA9IG1lYW4oQ1BNKSwgLmdyb3VwcyA9ICJkcm9wX2xhc3QiKSAlPiUKICAgIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSBjb250cmFzdElELCB2YWx1ZXNfZnJvbSA9IG1lYW4pICU+JQogICAgZHBseXI6OnJlbG9jYXRlKGNvbnRyYXN0LCAuYWZ0ZXIgPSB0YXJnZXQpICU+JQogICAgZHBseXI6OnJlbmFtZV93aXRoKGNvbmNhdF9uYW1lLCAtZ2VuZUlEKSAlPiUKICAgIGRwbHlyOjpyZW5hbWVfd2l0aCguY29scyA9LWdlbmVJRCwgLmZuID0gfiBwYXN0ZTAoImF2Z18oIiwueCwiKSIpKQogIAogIGRpZmZHZW5lcy5kZiAlPiUKICAgIGRwbHlyOjpzZWxlY3QoZ2VuZUlELCBzdGFydHNfd2l0aChwYXN0ZTAodFMsIi0iKSksIAogICAgICAgICAgICAgICAgICBzdGFydHNfd2l0aChwYXN0ZTAoY1MsIi0iKSkpICU+JQogICAgbGVmdF9qb2luKGdyb3VwQXZncywgYnkgPSAiZ2VuZUlEIikgJT4lCiAgICBsZWZ0X2pvaW4obGlzdC5teVRvcEhpdHMuZGZbW3ldXSwuLCBieSA9ICJnZW5lSUQiKSAlPiUKICAgIGxlZnRfam9pbihkcGx5cjo6c2VsZWN0KGRpZmZEZXNjLGdlbmVJRCxjb21wYXJpc29uW3ldKSwgYnkgPSAiZ2VuZUlEIikgJT4lCiAgICBkcGx5cjo6cmVuYW1lKERFR19EZXNjPWNvbXBhcmlzb25beV0pICU+JQogICAgZHBseXI6OnJlbG9jYXRlKERFR19EZXNjKSAlPiUKICAgIGRwbHlyOjpyZWxvY2F0ZShsb2dGQzpDZV9wZXJjZW50X2hvbW9sb2d5LCAuYWZ0ZXIgPSBsYXN0X2NvbCgpKQogIAp9LApzaW1wbGlmeSA9IEZBTFNFKQoKY29tcGFyaXNvbiA8LSBnc3ViKCIvWzAtOV0qIiwiIiwgY29tcGFyaXNvbikKbmFtZXMobGlzdC5teVRvcEhpdHMuZGYpIDwtIGNvbXBhcmlzb24KCmxpc3QubXlUb3BIaXRzLmRmIDwtIHNhcHBseShjb21wYXJpc29uLCBmdW5jdGlvbih5KXsKICBsaXN0Lm15VG9wSGl0cy5kZltbeV1dICU+JQogICAgZHBseXI6Om11dGF0ZShERUdfRGVzYyA9IGNhc2Vfd2hlbihERUdfRGVzYyA9PSAiVXAiIH4gcGFzdGUwKCJVcCBpbiAiLCBzdHJfc3BsaXQoeSwnLScsc2ltcGxpZnkgPSBUKVsxLDFdKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgREVHX0Rlc2MgPT0gIkRvd24iIH4gcGFzdGUwKCJEb3duIGluICIsIHN0cl9zcGxpdCh5LCctJyxzaW1wbGlmeSA9IFQpWzEsMV0pLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBERUdfRGVzYyA9PSAiTm90U2lnIiB+ICJOb3RTaWciKSkgCn0sCnNpbXBsaWZ5ID0gRkFMU0UsIApVU0UuTkFNRVMgPSBUUlVFKQoKIyBQQzEgVm9sY2FubyBQbG90IGFuZCBJbnRlcmFjdGl2ZSBUYWJsZSAtLS0tCnZwbG90MSA8LSBnZ3Bsb3QobGlzdC5teVRvcEhpdHMuZGZbWzFdXSkgKwogIGFlcyh5PS1sb2cxMChCSC5hZGouUC5WYWwpLCB4PWxvZ0ZDLCB0ZXh0ID0gcGFzdGUoZ2VuZUlELCAiPGJyPiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9nRkM6Iiwgcm91bmQobG9nRkMsIGRpZ2l0cyA9IDIpLCAiPGJyPiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicC12YWw6IiwgZm9ybWF0KEJILmFkai5QLlZhbCwgZGlnaXRzID0gMywgc2NpZW50aWZpYyA9IFRSVUUpKSkgKwogIGdlb21fcG9pbnQoc2l6ZT0yKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gLWxvZzEwKGFkai5QLnRocmVzaCksIAogICAgICAgICAgICAgbGluZXR5cGU9ImxvbmdkYXNoIiwgCiAgICAgICAgICAgICBjb2xvdXI9ImdyZXkiLCAKICAgICAgICAgICAgIHNpemU9MSkgKyAKICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBsZmMudGhyZXNoLCAKICAgICAgICAgICAgIGxpbmV0eXBlPSJsb25nZGFzaCIsIAogICAgICAgICAgICAgY29sb3VyPSIjQkU2ODREIiwgCiAgICAgICAgICAgICBzaXplPTEpICsKICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSAtbGZjLnRocmVzaCwgCiAgICAgICAgICAgICBsaW5ldHlwZT0ibG9uZ2Rhc2giLCAKICAgICAgICAgICAgIGNvbG91cj0iIzJDNDY3QSIsIAogICAgICAgICAgICAgc2l6ZT0xKSArCiAgbGFicyh0aXRsZSA9IHBhc3RlMCgnUGFpcndpc2UgQ29tcGFyaXNvbjogJywKICAgICAgICAgICAgICAgICAgICAgIGdzdWIoJy0nLAogICAgICAgICAgICAgICAgICAgICAgICAgICAnIHZzICcsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbXBhcmlzb25bMV0pKSwKICAgICAgIHN1YnRpdGxlID0gcGFzdGUwKCJncmV5IGxpbmU6IHAgPSAiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWRqLlAudGhyZXNoLCAiOyBjb2xvcmVkIGxpbmVzOiBsb2ctZm9sZCBjaGFuZ2UgPSAiLCBsZmMudGhyZXNoKSwKICAgICAgIGNvbG9yID0gIkdlbmVJRHMiKSArCiAgdGhlbWVfUHVibGljYXRpb24oKSAKdnBsb3QxCgojIEludGVyYWN0aXZlIFRhYmxlcwp5PC0gMQp0UzwtIHRhcmdldFN0YWdlW3ksXVt0YXJnZXRTdGFnZVt5LF0hPSIiXQpjUzwtIGNvbnRyYXN0U3RhZ2VbeSxdW2NvbnRyYXN0U3RhZ2VbeSxdIT0iIl0KCnNhbXBsZS5udW0udFMgPC0gc2FwcGx5KHRTLCBmdW5jdGlvbih4KSB7Y29sU3Vtcyh2LkRFR0xpc3QuZmlsdGVyZWQubm9ybSRkZXNpZ24pW1t4XV19KSAlPiUgc3VtKCkKc2FtcGxlLm51bS5jUyA8LSBzYXBwbHkoY1MsIGZ1bmN0aW9uKHgpIHtjb2xTdW1zKHYuREVHTGlzdC5maWx0ZXJlZC5ub3JtJGRlc2lnbilbW3hdXX0pICU+JSBzdW0oKQoKCm5fbnVtX2NvbHMgPC0gc2FtcGxlLm51bS50UyArIHNhbXBsZS5udW0uY1MgKyA1CkxTLmRhdGF0YWJsZSA8LSBsaXN0Lm15VG9wSGl0cy5kZltbeV1dICU+JQogIERUOjpkYXRhdGFibGUocm93bmFtZXMgPSBGQUxTRSwKICAgICAgICAgICAgICAgIGNhcHRpb24gPSBodG1sdG9vbHM6OnRhZ3MkY2FwdGlvbigKICAgICAgICAgICAgICAgICAgc3R5bGUgPSAnY2FwdGlvbi1zaWRlOiB0b3A7IHRleHQtYWxpZ246IGxlZnQ7IGNvbG9yOiBibGFjaycsCiAgICAgICAgICAgICAgICAgIGh0bWx0b29sczo6dGFncyRiKCdEaWZmZXJlbnRpYWxseSBFeHByZXNzZWQgR2VuZXMgaW4nLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaHRtbHRvb2xzOjp0YWdzJGVtKCdTLiBzdGVyY29yYWxpcycpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3N1YignLScsJyB2cyAnLGNvbXBhcmlzb25beV0pKSwKICAgICAgICAgICAgICAgICAgaHRtbHRvb2xzOjp0YWdzJGJyKCksCiAgICAgICAgICAgICAgICAgICJUaHJlc2hvbGQ6IHAgPCAiLAogICAgICAgICAgICAgICAgICBhZGouUC50aHJlc2gsICI7IGxvZy1mb2xkIGNoYW5nZSA+ICIsCiAgICAgICAgICAgICAgICAgIGxmYy50aHJlc2gsCiAgICAgICAgICAgICAgICAgIGh0bWx0b29sczo6dGFncyRicigpLAogICAgICAgICAgICAgICAgICAnVmFsdWVzID0gbG9nMiBjb3VudHMgcGVyIG1pbGxpb24nKSwKICAgICAgICAgICAgICAgIG9wdGlvbnMgPSBsaXN0KGF1dG9XaWR0aCA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzY3JvbGxYID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNjcm9sbFkgPSAnMzAwcHgnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2Nyb2xsQ29sbGFwc2UgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb3JkZXIgPSBsaXN0KG5fbnVtX2NvbHMtMSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ2Rlc2MnKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlYXJjaEhpZ2hsaWdodCA9IFRSVUUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFnZUxlbmd0aCA9IDI1LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxlbmd0aE1lbnUgPSBjKCI1IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIxMCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiMjUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjUwIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIxMDAiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbHVtbkRlZnMgPSBsaXN0KAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsaXN0KAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRhcmdldHMgPSAoKG5fbnVtX2NvbHMgKyAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDEpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZW5kZXIgPSBKUygKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJmdW5jdGlvbihkYXRhLCByb3cpIHsiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImRhdGEudG9FeHBvbmVudGlhbCgxKTsiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIn0iKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsaXN0KAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRhcmdldHMgPSAoKG5fbnVtX2NvbHMgKyAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDQpOihuX251bV9jb2xzICsgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA1KSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVuZGVyID0gSlMoCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiZnVuY3Rpb24oZGF0YSwgdHlwZSwgcm93LCBtZXRhKSB7IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyZXR1cm4gdHlwZSA9PT0gJ2Rpc3BsYXknICYmIGRhdGEubGVuZ3RoID4gMjAgPyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiJzxzcGFuIHRpdGxlPVwiJyArIGRhdGEgKyAnXCI+JyArIGRhdGEuc3Vic3RyKDAsIDIwKSArICcuLi48L3NwYW4+JyA6IGRhdGE7IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ9IikKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGlzdCh0YXJnZXRzID0gIl9hbGwiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNsYXNzPSJkdC1yaWdodCIpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICkpIApMUy5kYXRhdGFibGUgPC0gTFMuZGF0YXRhYmxlICU+JQogIERUOjpmb3JtYXRSb3VuZChjb2x1bW5zPWMoMzpuX251bV9jb2xzKSwgCiAgICAgICAgICAgICAgICAgIGRpZ2l0cz0zKQoKTFMuZGF0YXRhYmxlIDwtIExTLmRhdGF0YWJsZSAlPiUKICBEVDo6Zm9ybWF0Um91bmQoY29sdW1ucz1jKG5fbnVtX2NvbHMrMiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBuX251bV9jb2xzKzgpLCAKICAgICAgICAgICAgICAgICAgZGlnaXRzPTIpCgpMUy5kYXRhdGFibGUgPC0gTFMuZGF0YXRhYmxlICU+JQogIERUOjpmb3JtYXRTaWduaWYoY29sdW1ucz1jKG5fbnVtX2NvbHMrMSksIAogICAgICAgICAgICAgICAgICAgZGlnaXRzPTMpCgpMUy5kYXRhdGFibGUKCmBgYAoKIyBCZW5jaG1hcmtpbmcKYGBge3IgYmVuY2htYXJraW5nfQoKc3VwcHJlc3NQYWNrYWdlU3RhcnR1cE1lc3NhZ2VzKHsKICBsaWJyYXJ5KG9wZW54bHN4KQogIGxpYnJhcnkodGlkeXZlcnNlKQogIGxpYnJhcnkoZ2dwbG90MikKfSkKIyBMb2FkIEh1bnQgRGF0YXNldDogaUwzIHZzIEZMRiBjb21wYXJpc29uCnRlbXAuZGF0IDwtICByZWFkLnhsc3ggKCIuLi9CZW5jaG1hcmtpbmcvbmcuMzQ5NS1TNS54bHN4IiwgCiAgICAgICAgICAgICAgICAgICAgICAgIHNoZWV0ID0gNSwgc3RhcnRSb3cgPSAyKQoKSHVudC5kYXQgPC0gdGliYmxlKGdlbmVJRCA9IHRlbXAuZGF0JFgyLCBsb2dGQyA9IHRlbXAuZGF0JGxvZ0ZDKQpybSh0ZW1wLmRhdCkKCiMgUmVuYW1lIFJlc3VsdHMgb2YgaUwzIHZzIEZMRiBjb21wYXJpc29uIGZyb20gQnJvd3NlcgpCcm93c2VyLmRhdCA8LSBsaXN0Lm15VG9wSGl0cy5kZiRgKGlMMyktKEZMRilgICU+JQogIGRwbHlyOjpzZWxlY3QoZ2VuZUlELCBsb2dGQykKCnByaW50KHBhc3RlKCdUb3RhbCBudW1iZXIgb2YgZ2VuZXMgaW4gSHVudCAqZXQgYWwqIDIwMTYgaUwzIHZzIEZMRiBjb21wYXJpc29uIHRhYjonLG5yb3coSHVudC5kYXQpKSkKcHJpbnQocGFzdGUoJ1RvdGFsIG51bWJlciBvZiBnZW5lcyBpbiBTdHItUk5Bc2VxIEJyb3dzZXIgaUwzIHZzIEZMRiBvdXRwdXQgZmlsZTonLCBucm93KEJyb3dzZXIuZGF0KSkpIAoKCiMgVGhlIHBsb3QgYmVsb3cgdGFrZXMgdGhlIGdlbmVzIHdpdGggTG9nRkMgcmVzdWx0cyBpbiBib3RoIHRoZSBCcm93c2VyIGFuZCBIdW50IGRhdGFiYXNlcywgYW5kIHBsb3RzIHRoZSB0d28gc2V0cyBhZ2FpbnN0IGVhY2ggb3RoZXIuIApwbG90dGluZy5hbGwgPC0gaW5uZXJfam9pbihCcm93c2VyLmRhdCwgSHVudC5kYXQsIGJ5ID0gImdlbmVJRCIpCmdncGxvdChwbG90dGluZy5hbGwsIGFlcyh4ID0gbG9nRkMueCwgeSA9IGxvZ0ZDLnkpKSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kPWxtLCBjb2xvciA9ICdyZWQnLCBmb3JtdWxhID0gInkgfiB4IikgKwogIGdlb21fcG9pbnQoc2hhcGU9MTYsIHNpemU9MywgYWxwaGEgPSAwLjgpICsKICBsYWJzKHRpdGxlID0gIkFsbCBPdmVybGFwcGluZyBHZW5lczogU3RyLUJyb3dzZXIgdnMgSHVudCBEYXRhIiwKICAgICAgIHN1YnRpdGxlID0gImlMMyB2cyBGTEYiLAogICAgICAgY2FwdGlvbiA9ICJwb2ludHMgPSBnZW5lczsgcmVkIGxpbmUgPSBsaW5lYXIgbW9kZWwgKHkgfiB4KSIsCiAgICAgICB4ID0gIlN0ci1Ccm93c2VyIExvZ0ZDIiwKICAgICAgIHkgPSAiSHVudCBMb2dGQyIpICsKICBjb29yZF9lcXVhbCgpICsKICB0aGVtZV9idygpCgoKYGBgCgojIENsdXN0ZXIgREVHcyBpbnRvIGZ1bmN0aW9uYWwgbW9kdWxlcyAtLS0KYGBge3IgaGVhdG1hcHMsIGV2YWw9VFJVRSwgaW5jbHVkZT1GfQojIEludHJvZHVjdGlvbiB0byB0aGlzIGNodW5rIC0tLS0KIyB0aGlzIGNodW5rIGNyZWF0ZXMgaGVhdG1hcHMgZnJvbSBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgZ2VuZXM7CiMgaXQgdGFrZXMgYXMgaW5wdXQgYSBsaXN0IG9mIGdlbmVzIHRoYXQgYXJlIGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBpbiBhbnkgbGlmZSBzdGFnZQojIEl0IHNlbGVjdHMgbW9kdWxlcyBvZiBjby1leHByZXNzZWQgZ2VuZXMgYmFzZWQgb24gcGVhcnNvbiBjb3JyZWxhdGlvbnMKIyAKIyBUaGVzZSBkYXRhL3Jlc3VsdHMgYXJlIGV4YW1wbGVzIG9mIHBvc3NpYmxlIGFuYWx5c2VzIHRoYXQgY2FuIGJlIHJ1biBvbiB0aGlzIGRhdGEuCgojIExvYWQgcGFja2FnZXMgLS0tLS0Kc3VwcHJlc3NQYWNrYWdlU3RhcnR1cE1lc3NhZ2VzKHsKICBsaWJyYXJ5KHRpZHl2ZXJzZSkKICBsaWJyYXJ5KGxpbW1hKQogIGxpYnJhcnkoUkNvbG9yQnJld2VyKQogIGxpYnJhcnkoZ3Bsb3RzKQogIGxpYnJhcnkoaGVhdG1hcGx5KQogIGxpYnJhcnkoZ2dwbG90MikKICBsaWJyYXJ5KGVnZykKICBsaWJyYXJ5KGRlbmRleHRlbmQpCiAgc291cmNlKCIuLi9TdHJvbmd5bG9pZGVzX1JOQXNlcV9Ccm93c2VyL1NlcnZlci9nZ2hlYXRtYXBfbG9jYWwuUiIpCn0pCgojIENob29zZSBhIGNvbG9yIHBhbGxldHRlIC0tLS0KI215aGVhdGNvbG9ycyA8LSByZXYoYnJld2VyLnBhbChuYW1lPSJSZEJ1Iiwgbj0xMSkpCm15aGVhdGNvbG9ycyA8LSBSZEJ1KDc1KQoKIyBTZWxlY3QgdGhlIGNvbXBhcmlzb24KeSA9IDEKCiMgR2VuZXJhdGUgdmFyaWFibGUgY29udGFpbmluZyBleHByZXNzaW9uIGRhdGEgZm9yIHRoZSB0aHJlc2hvbGRlZCBERUdzIApkaWZmR2VuZXMudGhyZXNoIDwtIHYuREVHTGlzdC5maWx0ZXJlZC5ub3JtJEVbcmVzdWx0c1sseV0gIT0wLF0KY29sbmFtZXMoZGlmZkdlbmVzLnRocmVzaCkgPC0gcGFzdGUwKHRhcmdldHMkZ3JvdXAsICItIiwgdGFyZ2V0cyRzYW1wbGUpCgoKIyBDbHVzdGVyIERFR3MgYWNyb3NzIHN0YWdlcyAtLS0tCiNiZWdpbiBieSBjbHVzdGVyaW5nIHRoZSBnZW5lcyAocm93cykgZm9yIGEgbGlzdCBvZiBnZW5lcyB0aGF0IGFyZSBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgaW4gYXQgbGVhc3Qgb25lIGxpZmUgc3RhZ2UKIyB1c2UgdGhlICdjb3InIGZ1bmN0aW9uIGFuZCB0aGUgcGVhcnNvbiBtZXRob2QgZm9yIGZpbmRpbmcgYWxsIHBhaXJ3aXNlIGNvcnJlbGF0aW9ucyBvZiBnZW5lcwojICcxLWNvcicgY29udmVydHMgdGhpcyB0byBhIDAtMiBzY2FsZSBmb3IgZWFjaCBvZiB0aGVzZSBjb3JyZWxhdGlvbnMsIHdoaWNoIGNhbiB0aGVuIGJlIHVzZWQgdG8gY2FsY3VsYXRlIGEgZGlzdGFuY2UgbWF0cml4IHVzaW5nICdhcy5kaXN0JwpjbHVzdFJvd3MgPC0gaGNsdXN0KGFzLmRpc3QoMS1jb3IodChkaWZmR2VuZXMudGhyZXNoKSwgbWV0aG9kPSJwZWFyc29uIikpLCBtZXRob2Q9ImNvbXBsZXRlIikgCiMgaGllcmFyY2hpY2FsIGNsdXN0ZXJpbmcgaXMgYSB0eXBlIG9mIHVuc3VwZXJ2aXNlZCBjbHVzdGVyaW5nLiAKIyBOT1RFOiB0aGlzIGNsdXN0ZXIgbWF5IHByb3ZpZGUgZGlmZmVyZW50IHJlc3VsdHMgdG8gb25lIGJhc2VkIG9uIGxvZzIuY3BtLmZpbHRlcmVkLm5vcm0gZGF0YSwgbGlrZWx5IGIvYyB0aGlzIHZlcnNpb24gaXMgc3BlY2lmY2FsbHkgZm9jdXNlZCBvbiBnZW5lcyB0aGF0IGFyZSBzaWduaWZpY2FudGx5IGRpZmZlcmVudCBiZXR3ZWVuIGNvbmRpdGlvbnMuCiMgUmVsYXRlZCBtZXRob2RzIGluY2x1ZGUgSy1tZWFucywgU09NLCBldGMgCiMgdW5zdXBlcnZpc2VkIG1ldGhvZHMgYXJlIGJsaW5kIHRvIHNhbXBsZS9ncm91cCBpZGVudGl0eQojIGluIGNvbnRyYXN0LCBzdXBlcnZpc2VkIG1ldGhvZHMgJ3RyYWluJyBvbiBhIHNldCBvZiBsYWJlbGVkIGRhdGEuICAKIyBzdXBlcnZpc2VkIGNsdXN0ZXJpbmcgbWV0aG9kcyBpbmNsdWRlIHJhbmRvbSBmb3Jlc3QsIGFuZCBhcnRpZmljaWFsIG5ldXJhbCBuZXR3b3JrcwoKIyBjbHVzdGVyIHNhbXBsZXMgKGNvbHVtbnMpCmNsdXN0Q29sdW1ucyA8LSBoY2x1c3QoYXMuZGlzdCgxLWNvcihkaWZmR2VuZXMudGhyZXNoLCBtZXRob2Q9InNwZWFybWFuIikpLCBtZXRob2Q9ImNvbXBsZXRlIikgI2NsdXN0ZXIgY29sdW1ucyBieSBzcGVhcm1hbiBjb3JyZWxhdGlvbgojbm90ZTogdXNlIFNwZWFybWFuLCBpbnN0ZWFkIG9mIFBlYXJzb24sIGZvciBjbHVzdGVyaW5nIHNhbXBsZXMgYmVjYXVzZSBpdCBnaXZlcyBlcXVhbCB3ZWlnaHQgdG8gaGlnaGx5IHZzIGxvd2x5IGV4cHJlc3NlZCB0cmFuc2NyaXB0cyBvciBnZW5lcwoKI0N1dCB0aGUgcmVzdWx0aW5nIHRyZWUgYW5kIGNyZWF0ZSBjb2xvciB2ZWN0b3IgZm9yIGNsdXN0ZXJzLiAgCm1vZHVsZS5hc3NpZ24gPC0gc3RhdHM6OmN1dHJlZShjbHVzdFJvd3MsIGs9MTQpICNUaGUgZGlmZkdlbmVzIGluZm8gaXMgYmFzZWQgb24gYSBwYWlyd2lzZSBjb21wYXJpc29uIGJldHdlZW4gYWxsIDcgbGlmZSBzdGFnZXMuIAoKIyBhc3NpZ24gYSBjb2xvciB0byBlYWNoIG1vZHVsZSAobWFrZXMgaXQgZWFzeSB0byBpZGVudGlmeSBhbmQgbWFuaXB1bGF0ZSkKbW9kdWxlLmNvbG9yIDwtIHJhaW5ib3cobGVuZ3RoKHVuaXF1ZShtb2R1bGUuYXNzaWduKSksIHN0YXJ0PTAuMSwgZW5kPTAuOSkgCm1vZHVsZS5jb2xvciA8LSBtb2R1bGUuY29sb3JbYXMudmVjdG9yKG1vZHVsZS5hc3NpZ24pXSAKCiMgIyBzaW1wbGZ5IGhlYXRtYXAgYnkgYXZlcmFnaW5nIHRoZSBiaW9sb2dpY2FsIHJlcGxpY2F0ZXMgYW5kIGRpc3BsYXkgb25seSBvbmUgY29sdW1uIHBlciBjb25kaXRpb24KIyBkaWZmR2VuZXMuQVZHIDwtIGF2ZWFycmF5cyhkaWZmR2VuZXMudGhyZXNoKQoKIyBwbG90IHRoZSBoY2x1c3QgcmVzdWx0cyBhcyBhIGhlYXRtYXAsIGdyb3VwaW5nIHRoZSBsaWZlIHN0YWdlcyB0b2dldGhlcgpkaWZmR2VuZXMuaGVhdG1hcCA8LSBoZWF0bWFwLjIoZGlmZkdlbmVzLnRocmVzaCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNydENvbCA9IDAsIGFkakNvbD0gYygwLjUsMC41KSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFJvd3Y9YXMuZGVuZHJvZ3JhbShjbHVzdFJvd3MpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQ29sdj1hcy5kZW5kcm9ncmFtKGNsdXN0Q29sdW1ucyksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBrZXkudGl0bGUgPSBOQSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1haW4gPSBwYXN0ZTAoIkRFRyBIZWF0bWFwIChieSBsaWZlIHN0YWdlKTogIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdWIgPSBwYXN0ZTAoIkdlbmVzIHBhc3MgdGhyZXNob2xkIGluID49IDEgY29tcGFyaXNvbi4gVGhyZXNob2xkOiBwIDwgIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhZGouUC50aHJlc2gsICI7IGxvZy1mb2xkIGNoYW5nZSA+ICIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGZjLnRocmVzaCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBSb3dTaWRlQ29sb3JzPW1vZHVsZS5jb2xvciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbD1yZXYobXloZWF0Y29sb3JzKSwgc2NhbGU9J3JvdycsIGxhYlJvdz1OQSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlbnNpdHkuaW5mbz0ibm9uZSIsIHRyYWNlPSJub25lIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNleFJvdz0xLCBjZXhDb2w9MSkKCiMjIEdHUGxvdHMgdmVyc2lvbgojIGdnLmRpZmZHZW5lcy5oZWF0bWFwPC1nZ2hlYXRtYXBfbG9jYWwoZGlmZkdlbmVzLnRocmVzaCwKIyAgICAgICAgICAgICAgICAgICAgY29sb3JzID0gcmV2KG15aGVhdGNvbG9ycyksCiMgICAgICAgICAgICAgICAgICAgIFJvd3Y9IGxhZGRlcml6ZShhcy5kZW5kcm9ncmFtKGNsdXN0Um93cykpLAojICAgICAgICAgICAgICAgICAgICBDb2x2PWxhZGRlcml6ZShhcy5kZW5kcm9ncmFtKGNsdXN0Q29sdW1ucykpLAojICAgICAgICAgICAgICAgICAgICBrZXkudGl0bGUgPSAiTG9nMkNQTSIsCiMgICAgICAgICAgICAgICAgICAgIGJyYW5jaGVzX2x3ZCA9IDAuMiwKIyAgICAgICAgICAgICAgICAgICAgc2hvd3RpY2tsYWJlbHMgPSBjKFRSVUUsIEZBTFNFKSwKIyAgICAgICAgICAgICAgICAgICAgc2NhbGU9J3JvdycsCiMgICAgICAgICAgICAgICAgICAgIGNleFJvdz0xLCBjZXhDb2w9MSkKCiMgZ2dzYXZlKCIuL2hlYXRtYXAucGRmIiwgcGxvdCA9IGdnLmhlYXRtYXAsIHdpZHRoID0gMTEsIGhlaWdodCA9IDgsIHVuaXRzID0gImluIiwgZGV2aWNlID0gInBkZiIpCiMgTWFrZSBhbiBpbnRlcmFjdGl2ZSB2ZXJzaW9uCiMgaW50ZXJhY3RpdmUuZGlmZkdlbmVzLmhlYXRtYXAgPC0gaGVhdG1hcGx5KGRpZmZHZW5lcy50aHJlc2gsCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3JzID0gcmV2KG15aGVhdGNvbG9ycyksCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUm93dj0gbGFkZGVyaXplKGFzLmRlbmRyb2dyYW0oY2x1c3RSb3dzKSksCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQ29sdj1sYWRkZXJpemUoYXMuZGVuZHJvZ3JhbShjbHVzdENvbHVtbnMpKSwKIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaG93dGlja2xhYmVscyA9IGMoVFJVRSwgRkFMU0UpLAojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNjYWxlPSdyb3cnLAojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBsb3RfbWV0aG9kID0gImdncGxvdCIsCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJhbmNoZXNfbHdkID0gMC4yLAojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGtleS50aXRsZSA9ICJMb2cyQ1BNIiwKIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjZXhSb3c9MSwgY2V4Q29sPTEpCgpgYGAKCiMgRnVuY3Rpb25hbCBFbnJpY2htZW50IEFuYWx5c2lzIC0tLQpgYGB7ciBmdW5jdGlvbmFsRW5yaWNobWVudH0KIyBMb2FkIHBhY2thZ2VzIC0tLS0Kc3VwcHJlc3NQYWNrYWdlU3RhcnR1cE1lc3NhZ2VzKHsKICBsaWJyYXJ5KHRpZHl2ZXJzZSkKICBsaWJyYXJ5KGxpbW1hKQogIGxpYnJhcnkob3Blbnhsc3gpCiAgbGlicmFyeShncGxvdHMpICNmb3IgaGVhdG1hcHMKICBsaWJyYXJ5KERUKSAjaW50ZXJhY3RpdmUgYW5kIHNlYXJjaGFibGUgdGFibGVzIG9mIG91ciBHU0VBIHJlc3VsdHMKICBsaWJyYXJ5KEdTRUFCYXNlKSAjZnVuY3Rpb25zIGFuZCBtZXRob2RzIGZvciBHZW5lIFNldCBFbnJpY2htZW50IEFuYWx5c2lzCiAgbGlicmFyeShCaW9iYXNlKSAjYmFzZSBmdW5jdGlvbnMgZm9yIGJpb2NvbmR1Y3RvcjsgcmVxdWlyZWQgYnkgR1NFQUJhc2UKICBsaWJyYXJ5KEdTVkEpICNHZW5lIFNldCBWYXJpYXRpb24gQW5hbHlzaXMsIGEgbm9uLXBhcmFtZXRyaWMgYW5kIHVuc3VwZXJ2aXNlZCBtZXRob2QgZm9yIGVzdGltYXRpbmcgdmFyaWF0aW9uIG9mIGdlbmUgc2V0IGVucmljaG1lbnQgYWNyb3NzIHNhbXBsZXMuCiAgbGlicmFyeShncHJvZmlsZXIyKSAjdG9vbHMgZm9yIGFjY2Vzc2luZyB0aGUgR08gZW5yaWNobWVudCByZXN1bHRzIHVzaW5nIGc6UHJvZmlsZXIgd2ViIHJlc291cmNlcwogIGxpYnJhcnkoY2x1c3RlclByb2ZpbGVyKSAjIHByb3ZpZGVzIGEgc3VpdGUgb2YgdG9vbHMgZm9yIGZ1bmN0aW9uYWwgZW5yaWNobWVudCBhbmFseXNpcwogIGxpYnJhcnkobXNpZ2RicikgIyBhY2Nlc3MgdG8gbXNpZ2RiIGNvbGxlY3Rpb25zIGRpcmVjdGx5IHdpdGhpbiBSCiAgbGlicmFyeShlbnJpY2hwbG90KSAjIGdyZWF0IGZvciBtYWtpbmcgdGhlIHN0YW5kYXJkIEdTRUEgZW5yaWNobWVudCBwbG90cwp9KQojIFBpY2sgYSBwYWlyd2lzZSBjb21wYXJpc29uCnl5IDwtIDEKCiMgQ2Fycnkgb3V0IEdPIGVucmljaG1lbnQgdXNpbmcgZ1Byb2ZpbGVyMiAtLS0tCiMgR08gZW5yaWNobWVudCByZXF1aXJlcyBhIHByZS1zZWxlY3RlZCBzZXQgb2YgZ2VuZXMuIENhbiB1c2UgbXVsdGlwbGUgY3JpdGVyaWEgdG8gZG8gdGhhdCBpbml0aWFsIHNlbGVjdGlvbi4KIyBUaGUgR08gdGVybXMgSSdtIGFjY2Vzc2luZyB1c2luZyB0aGUgZ29zdCBhcmUgZnJvbSBIdW50IGV0IGFsIDIwMTYsIEkgYmVsaWV2ZS4KCiMgIyBQQzEgVG9wVGFibGUgUmVzdWx0cwojIGVucmljaGVkLnNldC5wb3MgPC1saXN0Lm15VG9wSGl0cy5kZltbeXldXSAlPiUgCiMgICAgIHNsaWNlX21heChsb2dGQywgcHJvcCA9IC4xKSAjIGdldCB0b3AgMTAlIG9mIGdlbmVzCiMgCiMgZW5yaWNoZWQuc2V0Lm5lZyA8LSBsaXN0Lm15VG9wSGl0cy5kZltbeXldXSAlPiUgCiMgICAgIHNsaWNlX21pbihsb2dGQywgcHJvcCA9IC4xKSAjIGdldCB0b3AgMTAlIG9mIGdlbmVzCiMgCiMgZ29zdC5yZXMucG9zIDwtIGdvc3QobGlzdChUYXJnZXRfVXByZWd1bGF0ZWQgPSBlbnJpY2hlZC5zZXQucG9zJGdlbmVJRCksIG9yZ2FuaXNtID0gInN0c3RlcnByamViNTI4IiwgY29ycmVjdGlvbl9tZXRob2QgPSAiZmRyIikKIyBnb3N0cGxvdChnb3N0LnJlcy5wb3MsIGludGVyYWN0aXZlID0gVCwgY2FwcGVkID0gVCkKIyAKIyBnb3N0LnJlcy5uZWcgPC0gZ29zdChsaXN0KFRhcmdldF9Eb3ducmVndWxhdGVkX0dlbmVzID0gZW5yaWNoZWQuc2V0Lm5lZyRnZW5lSUQpLCBvcmdhbmlzbSA9ICJzdHN0ZXJwcmplYjUyOCIsIGNvcnJlY3Rpb25fbWV0aG9kID0gImZkciIpCiMgZ29zdHBsb3QoZ29zdC5yZXMubmVnLCBpbnRlcmFjdGl2ZSA9IFQsIGNhcHBlZCA9IFQpCgojIFBlcmZvcm0gR1NFQSB1c2luZyBjbHVzdGVyUHJvZmlsZXIgLS0tLQojIFdoaWNoIGxpYnJhcnkgdG8gdXNlIGZvciBpbXBsZW1lbnRhdGlvbj8gQXMgcGVyIGh0dHBzOi8vYWNhZGVtaWMub3VwLmNvbS9iaWIvYWR2YW5jZS1hcnRpY2xlL2RvaS8xMC4xMDkzL2JpYi9iYnoxNTgvNTcyMjM4NDogIkZvciBleHByZXNzaW9uLWJhc2VkIEVBIG9uIHRoZSBmdWxsIGV4cHJlc3Npb24gbWF0cml4Li4uV2hlbiBnaXZlbiByYXcgcmVhZCBjb3VudHMsIHdlIHJlY29tbWVuZCB0byBhcHBseSBhIFZTVCBzdWNoIGFzIHZvb20gWzM5XSB0byBhcnJpdmUgYXQgbGlicmFyeS1zaXplIG5vcm1hbGl6ZWQgbG9nQ1BNcy4iCiMgRm9yIHRlc3Rpbmcgc2VsZi1jb250YWluZWQgbnVsbCBoeXBvdGhlc2lzICh0ZXN0IGZvciBhc3NvY2lhdGlvbiBvZiBhbnkgZ2VuZSBpbiB0aGUgc2V0IHdpdGggdGhlIHBoZW5vdHlwZSksIHVzZSBST0FTVAojIEZvciB0ZXN0aW5nIGNvbXBldGl0aXZlIG51bGwgaHlwb3RoZXNpcyAodGVzdCBmb3IgZXhjZXNzIG9mIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIGluIGEgZ2VuZSBzZXQgcmVsYXRpdmUgdG8gZ2VuZXMgb3V0c2lkZSB0aGUgc2V0KSAtICoqdGhlaXIgcmVjb21tZW5kYXRpb24qKiwgdXNlIFBBRE9HIG9yIFNBRkU/CiMgCiMgQWJpbGl0eSB0byBkbyB0aGlzIGRlcGVuZHMgb24gdGhlIGF2YWlsYWJpbGl0eSBvZiBnZW5lIHNldHMuIE1ham9yIGRhdGFiYXNlcyAoZS5nLiBtc2lnZGIgZG9uJ3Qgc2VlbSB0byBoYXZlIHN0ZXJjb3JhbGlzIGluZm9ybWF0aW9uLiBUaGV5IGRvIGhhdmUgQy4gZWxlZ2FucyBnZW5lIHNldHMsIGJ1dCBJJ20gbm90IGNvbnZpbmNlZCB0aGUgaG9tb2xvZ3kgaW5mb3JtYXRpb24gaXMgZ29vZCBlbm91Z2ggZm9yIHRoZSBjb21wYXJpc29uIHRvIGJlIHVuYmlhc2VkL21lYW5pbmdmdWwuIAojIAojIEFsdGhvdWdoIHRoZSBMb2sgbGFiIGRpZCBHU0VBIGluIFJhbWFuYXRoYW4gKmV0IGFsKiAyMDExIChQTUlEOiAyMTU3MjUyNCksIHVzaW5nIEMuIGVsZWdhbnMgaG9tb2xvZ3MgYXJlIHR3byBtYW51YWxseSBjb21waWxlZCBnZW5lIHNldHM6IAojIDEpIDMxIGdlbmVzIHdpdGggcHJvZHVjdHMga25vd24gdG8gYmUgaW1tdW5vcmVhY3RpdmUgaW4gKlMuIHN0ZXJjb3JhbGlzKi1pbmZlY3RlZCBodW1hbnMKIyAyKSA0MiBwdXRhdGl2ZWx5IGlkZW50aWZpZWQgaGVhdCBzaG9jayBwcm90ZWlucwojIE9mIGNvdXJzZSwgdGhpcyBpcyBiZWZvcmUgdGhlIGdlbm9tZSB3YXMgc2VxdWVuY2VkLCBzbyB0aGVzZSBnZW5lcyBkb24ndCBoYXZlIGFzc29jaWF0ZWQgU1NUUCBudW1iZXJzLiAKIyAKIyBJbiBIdW50IGV0IGFsIDIwMTYsIHRoZXJlIGlzIGFuIEVuc2VtYmwgQ29tcGFyYSBwcm90ZWluIGZhbWlseSBzZXQKIyBOb3RlIHRoYXQgdGhpcyB1c2VzIHNwZWNpZmljIHRyYW5zY3JpcHQgaW5mb3JtYXRpb24sIHdoaWNoIEkgdGhyb3cgb3V0LiAKIyAoZS5nLiBTU1RQXzAwMDExMzc0MDAuMiBpcyByZWNvZGVkIGFzIFNTVFBfMDAwMTEzNzQwMCkKZW5zQ29tcC5nZW5lSURzIDwtIHJlYWQueGxzeCAoIi4uL0RhdGEvSHVudF9QYXJhc2l0ZV9FbnNlbWJsX0NvbXBhcmEueGxzeCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaGVldCA9IDEpICU+JQogIGFzX3RpYmJsZSgpICU+JQogIGRwbHlyOjpzZWxlY3QoLUZhbWlseS5tZW1iZXJzKSAlPiUKICBwaXZvdF9sb25nZXIoY29scyA9IC1Db21wYXJhLmZhbWlseS5pZCwgdmFsdWVzX3RvID0gImdlbmVJRCIpICU+JQogIGRwbHlyOjpzZWxlY3QoLW5hbWUpICU+JQogIGRwbHlyOjpmaWx0ZXIoZ3JlcGwoIlNTVFBfIiwgZ2VuZUlEKSkKCmVuc0NvbXAuZ2VuZUlEcyRnZW5lSUQgPC0gc3RyX3JlbW92ZV9hbGwoZW5zQ29tcC5nZW5lSURzJGdlbmVJRCwgIi8vLlswLTldJCIpCgojIENvbXBhcmUgdGhlc2UgZ2VuZXMgdG8gdGhlIGxpc3Qgb2YgZ2VuZXMgaW4gb3VyIGZpbHRlcmVkLCBub3JtYWxpemVkIGxpc3QgLS0tLQojIApjb21wYXJhLmV4Y2x1c2l2ZSA8LSB1bmlxdWUoZW5zQ29tcC5nZW5lSURzJGdlbmVJRCkgJT4lCiAgYXNfdGliYmxlX2NvbChjb2x1bW5fbmFtZSA9ICJnZW5lSUQiKSAlPiUKICBkcGx5cjo6YW50aV9qb2luKGRpZmZHZW5lcy5kZiwgYnkgPSAiZ2VuZUlEIikKbnJvdyhjb21wYXJhLmV4Y2x1c2l2ZSkKCmNvbXBhcmEuYWJzZW50IDwtIHVuaXF1ZShlbnNDb21wLmdlbmVJRHMkZ2VuZUlEKSAlPiUKICBhc190aWJibGVfY29sKGNvbHVtbl9uYW1lID0gImdlbmVJRCIpICU+JQogIGRwbHlyOjphbnRpX2pvaW4oZGlmZkdlbmVzLmRmLC4sIGJ5ID0gImdlbmVJRCIpICU+JQogIGRwbHlyOjpzZWxlY3QoZ2VuZUlEKQpucm93KGNvbXBhcmEuYWJzZW50KQoKIyBIb3cgbWFueSBnZW5lcyBoYXZlIGFzc29jaWF0ZWQgR08gdGVybXM/IC0tLS0KR08ucHJlc2VudCA8LSBsaXN0Lm15VG9wSGl0cy5kZltbeXldXSRHT190ZXJtICU+JQogIGdzdWIoIk5BIiwgTkEsLikgJT4lCiAgYXNfdGliYmxlX2NvbChjb2x1bW5fbmFtZSA9ICJHT19UZXJtIikgJT4lCiAgdGliYmxlKGdlbmVJRCA9IGxpc3QubXlUb3BIaXRzLmRmW1t5eV1dJGdlbmVJRCwuKSAlPiUKICBkcGx5cjo6ZmlsdGVyKCFpcy5uYShHT19UZXJtKSkKbnJvdyhHTy5wcmVzZW50KQoKIyBBcmUgYW55IG9mIHRoZXNlIGdlbmVzIHBhcnQgb2YgdGhvc2Ugbm90IGZvdW5kIGluIHRoZSBjb21wYXJhIGRhdGFzZXQ/IC0tLS0gCkdPLnByZXNlbnQuQ29tcGFyYS5hYnNlbnQgPC0gZHBseXI6OnNlbWlfam9pbihHTy5wcmVzZW50LCBjb21wYXJhLmFic2VudCwgYnkgPSAiZ2VuZUlEIikKbnJvdyhHTy5wcmVzZW50LkNvbXBhcmEuYWJzZW50KQoKIyBNYWtlIGEgbGlzdCBvZiBnZW5lcwplbnNDb21wLmZhbWlseUlEcyA8LSByZWFkLnhsc3ggKCIuLi9EYXRhL0h1bnRfUGFyYXNpdGVfRW5zZW1ibF9Db21wYXJhLnhsc3giLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaGVldCA9IDIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29scyA9IGMoMSw0OjYpKSAlPiUKICBhc190aWJibGUoKSAlPiUKICBkcGx5cjo6bXV0YXRlKEZhbWlseV9EZXNjcmlwdGlvbiA9IGRwbHlyOjpjb2FsZXNjZSguJERlc2NyaXB0aW9uLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAuJGBUb3AucHJvZHVjdC4obWVtYmVycy53aXRoLmhpdClgLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAuJGBJbnRlcnByby50b3AuaGl0LihtZW1iZXJzLndpdGguaGl0KWApCiAgKSAlPiUKICBkcGx5cjo6c2VsZWN0KENvbXBhcmEuZmFtaWx5LmlkLCBGYW1pbHlfRGVzY3JpcHRpb24pCgplbnNDb21wIDwtIGxlZnRfam9pbihlbnNDb21wLmdlbmVJRHMsIGVuc0NvbXAuZmFtaWx5SURzLCBieSA9ICJDb21wYXJhLmZhbWlseS5pZCIpICU+JQogIGRwbHlyOjpzZWxlY3QoLUNvbXBhcmEuZmFtaWx5LmlkKSAlPiUKICBkcGx5cjo6cmVuYW1lKGdzX25hbWUgPSBGYW1pbHlfRGVzY3JpcHRpb24sIGdlbmVfc3ltYm9sID0gZ2VuZUlEKSAlPiUKICBkcGx5cjo6cmVsb2NhdGUoZ3NfbmFtZSwgZ2VuZV9zeW1ib2wpCgpybShlbnNDb21wLmdlbmVJRHMsIGVuc0NvbXAuZmFtaWx5SURzKQoKIyBTYXZlIGRhdGFiYXNlIG9mIHBhcmFzaXRlIEdlbmUgU2V0cyBmb3IgaW1wb3J0IGludG8gU2hpbnkgQXBwLiAtLS0tCgojIEZpbHRlciBvdXQgZ2VuZXMgdGhhdCBhcmVuJ3QgcGFydCBvZiBvdXIgUk5Bc2VxIGRhdGFzZXQKZ2VuZWxpc3QgPC0gdi5ERUdMaXN0LmZpbHRlcmVkLm5vcm0kZ2VuZXMgJT4lCiAgcm93bmFtZXNfdG9fY29sdW1uKHZhciA9ICJnZW5lSUQiKSAlPiUKICBkcGx5cjo6c2VsZWN0KGdlbmVJRCkKZW5zQ29tcDwtIGVuc0NvbXAgJT4lCiAgZHBseXI6OnJlbmFtZShnZW5lSUQgPSBnZW5lX3N5bWJvbCkgJT4lCiAgbGVmdF9qb2luKGdlbmVsaXN0LCAuLCBieSA9ICJnZW5lSUQiKSAlPiUKICBkcGx5cjo6cmVsb2NhdGUoZ3NfbmFtZSwgZ2VuZUlEKQoKc2F2ZShlbnNDb21wLAogICAgIGZpbGUgPSAiLi4vT3V0cHV0cy9wYXJhc2l0ZUdlbmVTZXRzIikKCiMgR2VuZXJhdGUgcmFuayBvcmRlcmVkIGxpc3Qgb2YgZ2VuZXMgLS0tLQpteWRhdGEuZGYuc3ViIDwtIGRwbHlyOjpzZWxlY3QobGlzdC5teVRvcEhpdHMuZGZbW3l5XV0sIGdlbmVJRCwgbG9nRkMpCm15ZGF0YS5nc2VhIDwtIG15ZGF0YS5kZi5zdWIkbG9nRkMKbmFtZXMobXlkYXRhLmdzZWEpIDwtIGFzLmNoYXJhY3RlcihteWRhdGEuZGYuc3ViJGdlbmVJRCkKbXlkYXRhLmdzZWEgPC0gc29ydChteWRhdGEuZ3NlYSwgZGVjcmVhc2luZyA9IFRSVUUpCgojIHJ1biBHU0VBIHVzaW5nIHRoZSAnR1NFQScgZnVuY3Rpb24gZnJvbSBjbHVzdGVyUHJvZmlsZXIKIyBHaXZlbiBhIHByaW9yaSBkZWZpbmVkIHNldCBvZiBnZW5lIFMgKGUuZy4sIGdlbmVzIHNoYXJlaW5nIHRoZSBzYW1lIERPIGNhdGVnb3J5KSwgdGhlIGdvYWwgb2YgR1NFQSBpcyB0byBkZXRlcm1pbmUgd2hldGhlciB0aGUgbWVtYmVycyBvZiBTIGFyZSByYW5kb21seSBkaXN0cmlidXRlZCB0aHJvdWdob3V0IHRoZSByYW5rZWQgZ2VuZSBsaXN0IChMKSBvciBwcmltYXJpbHkgZm91bmQgYXQgdGhlIHRvcCBvciBib3R0b20uCiMgVGhlcmUgYXJlIHRocmVlIGtleSBlbGVtZW50cyBvZiB0aGUgR1NFQSBtZXRob2Q6CiMgKipDYWxjdWxhdGlvbiBvZiBhbiBFbnJpY2htZW50IFNjb3JlLioqCiMgVGhlIGVucmljaG1lbnQgc2NvcmUgKEVTKSByZXByZXNlbnQgdGhlIGRlZ3JlZSB0byB3aGljaCBhIHNldCBTIGlzIG92ZXItcmVwcmVzZW50ZWQgYXQgdGhlIHRvcCBvciBib3R0b20gb2YgdGhlIHJhbmtlZCBsaXN0IEwuIFRoZSBzY29yZSBpcyBjYWxjdWxhdGVkIGJ5IHdhbGtpbmcgZG93biB0aGUgbGlzdCBMLCBpbmNyZWFzaW5nIGEgcnVubmluZy1zdW0gc3RhdGlzdGljIHdoZW4gd2UgZW5jb3VudGVyIGEgZ2VuZSBpbiBTIGFuZCBkZWNyZWFzaW5nIHdoZW4gaXQgaXMgbm90LiBUaGUgbWFnbml0dWRlIG9mIHRoZSBpbmNyZW1lbnQgZGVwZW5kcyBvbiB0aGUgZ2VuZSBzdGF0aXN0aWNzIChlLmcuLCBjb3JyZWxhdGlvbiBvZiB0aGUgZ2VuZSB3aXRoIHBoZW5vdHlwZSkuIFRoZSBFUyBpcyB0aGUgbWF4aW11bSBkZXZpYXRpb24gZnJvbSB6ZXJvIGVuY291bnRlcmVkIGluIHRoZSByYW5kb20gd2FsazsgaXQgY29ycmVzcG9uZHMgdG8gYSB3ZWlnaHRlZCBLb2xtb2dvcm92LVNtaXJub3YtbGlrZSBzdGF0aXN0aWMgKFN1YnJhbWFuaWFuIGV0IGFsLiAyMDA1KS4KIyAqKkVzaW1hdGlvbiBvZiBTaWduaWZpY2FuY2UgTGV2ZWwgb2YgRVMuKioKIyBUaGUgcC12YWx1ZSBvZiB0aGUgRVMgaXMgY2FsY3VsYXRlZCB1c2luZyBwZXJtdXRhdGlvbiB0ZXN0LiBTcGVjaWZpY2FsbHksIHdlIHBlcm11dGUgdGhlIGdlbmUgbGFiZWxzIG9mIHRoZSBnZW5lIGxpc3QgTCBhbmQgcmVjb21wdXRlIHRoZSBFUyBvZiB0aGUgZ2VuZSBzZXQgZm9yIHRoZSBwZXJtdXRhdGVkIGRhdGEsIHdoaWNoIGdlbmVyYXRlIGEgbnVsbCBkaXN0cmlidXRpb24gZm9yIHRoZSBFUy4gVGhlIHAtdmFsdWUgb2YgdGhlIG9ic2VydmVkIEVTIGlzIHRoZW4gY2FsY3VsYXRlZCByZWxhdGl2ZSB0byB0aGlzIG51bGwgZGlzdHJpYnV0aW9uLgojICoqQWRqdXN0bWVudCBmb3IgTXVsdGlwbGUgSHlwb3RoZXNpcyBUZXN0aW5nLioqCiMgV2hlbiB0aGUgZW50aXJlIGdlbmUgc2V0cyB3ZXJlIGV2YWx1YXRlZCwgRE9TRSBhZGp1c3QgdGhlIGVzdGltYXRlZCBzaWduaWZpY2FuY2UgbGV2ZWwgdG8gYWNjb3VudCBmb3IgbXVsdGlwbGUgaHlwb3RoZXNpcyB0ZXN0aW5nIGFuZCBhbHNvIHEtdmFsdWVzIHdlcmUgY2FsY3VsYXRlZCBmb3IgRkRSIGNvbnRyb2wuCm15R1NFQS5yZXMgPC0gR1NFQShteWRhdGEuZ3NlYSwgVEVSTTJHRU5FPWVuc0NvbXAsIHZlcmJvc2U9RkFMU0UpCm15R1NFQS5kZiA8LSBhc190aWJibGUobXlHU0VBLnJlc0ByZXN1bHQpCgpteUdTRUEudGJsPC1hc190aWJibGUobXlHU0VBLnJlc0ByZXN1bHQpICU+JQogIGRwbHlyOjpzZWxlY3QoLWMoRGVzY3JpcHRpb24sIHB2YWx1ZSwgZW5yaWNobWVudFNjb3JlKSkgJT4lCiAgZHBseXI6OnJlbmFtZShub3JtYWxpemVkX0VucmljaG1lbnRTY29yZSA9IE5FUykKCiMgdmlldyByZXN1bHRzIGFzIGFuIGludGVyYWN0aXZlIHRhYmxlCmVucmljaG1lbnQuRFQgPC0gZGF0YXRhYmxlKG15R1NFQS50YmwsIAogICAgICAgICAgICAgICAgICAgICAgICAgICByb3duYW1lcyA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGNhcHRpb24gPSAgaHRtbHRvb2xzOjp0YWdzJGNhcHRpb24oCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3R5bGUgPSAnY2FwdGlvbi1zaWRlOiB0b3A7IHRleHQtYWxpZ246IGxlZnQ7IGNvbG9yOiBibGFjaycsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaHRtbHRvb2xzOjp0YWdzJGIoJ0dlbmUgRmFtaWxpZXMgRW5yaWNoZWQgaW4gJywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3N1YignLScsJyB2cyAnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmFtZXMobGlzdC5teVRvcEhpdHMuZGYpW1t5eV1dKSkKICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgb3B0aW9ucyA9IGxpc3QoCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXV0b1dpZHRoID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzY3JvbGxYID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjc2Nyb2xsWSA9ICc4MDBweCcsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2Nyb2xsQ29sbGFwc2UgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlYXJjaEhpZ2hsaWdodCA9IFRSVUUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9yZGVyID0gbGlzdCgzLCAnZGVzYycpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhZ2VMZW5ndGggPSAyNSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGVuZ3RoTWVudSA9IGMoIjUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIxMCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjI1IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiNTAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIxMDAiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2x1bW5EZWZzID0gbGlzdCgKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxpc3QodGFyZ2V0cyA9ICJfYWxsIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2xhc3M9ImR0LXJpZ2h0IikpKSkgJT4lCiAgZm9ybWF0Um91bmQoY29sdW1ucz1jKDMsNTo2KSwgZGlnaXRzPTIpICU+JQogIGZvcm1hdFJvdW5kKGNvbHVtbnM9Yyg0KSwgZGlnaXRzPTQpCmVucmljaG1lbnQuRFQKCiMgY3JlYXRlIGVucmljaG1lbnQgcGxvdHMgdXNpbmcgdGhlIGVucmljaHBsb3QgcGFja2FnZQojIGdzZWFwbG90MihteUdTRUEucmVzLCAKIyAgICAgICAgICAgZ2VuZVNldElEID0gMywgI2NhbiBjaG9vc2UgbXVsdGlwbGUgc2lnbmF0dXJlcyB0byBvdmVybGF5IGluIHRoaXMgcGxvdAojICAgICAgICAgICBwdmFsdWVfdGFibGUgPSBGQUxTRSwgI2NhbiBzZXQgdGhpcyB0byBGQUxTRSBmb3IgYSBjbGVhbmVyIHBsb3QKIyAgICAgICAgICAgdGl0bGUgPSAiU0NQL1RBUCBHZW5lIFNldCIpICNjYW4gYWxzbyB0dXJuIG9mZiB0aGlzIHRpdGxlCgojIGFkZCBhIHZhcmlhYmxlIHRvIHRoaXMgcmVzdWx0IHRoYXQgbWF0Y2hlcyBlbnJpY2htZW50IGRpcmVjdGlvbiB3aXRoIHBoZW5vdHlwZQpteUdTRUEuZGYgPC0gbXlHU0VBLmRmICU+JQogIG11dGF0ZShsaWZlX3N0YWdlID0gY2FzZV93aGVuKAogICAgTkVTID4gMCB+IHN0cl9zcGxpdChuYW1lcyhsaXN0Lm15VG9wSGl0cy5kZilbW3l5XV0sJy0nLHNpbXBsaWZ5ID0gVClbMSwxXSwKICAgIE5FUyA8IDAgfiBzdHJfc3BsaXQobmFtZXMobGlzdC5teVRvcEhpdHMuZGYpW1t5eV1dLCctJyxzaW1wbGlmeSA9IFQpWzEsMl0pKQoKbXlHU0VBLmRmJElEIDwtIG15R1NFQS5kZiRJRCAlPiUKICB3b3JkKHNlcCA9ICcsJykgJT4lCiAgI3dvcmQoc2VwID0gJy8nKSAlPiUKICB3b3JkKHNlcCA9ICcgYW5kJykKCiMgY3JlYXRlICdidWJibGUgcGxvdCcgdG8gc3VtbWFyaXplIHkgc2lnbmF0dXJlcyBhY3Jvc3MgeCBwaGVub3R5cGVzCmdncGxvdChteUdTRUEuZGYsIGFlcyh4PWxpZmVfc3RhZ2UsIHk9SUQpKSArIAogIGdlb21fcG9pbnQoYWVzKHNpemU9c2V0U2l6ZSwgY29sb3IgPSBORVMsIGFscGhhPS1sb2cxMChwLmFkanVzdCkpKSArCiAgc2NhbGVfY29sb3JfZ3JhZGllbnQobG93PSJibHVlIiwgaGlnaD0icmVkIikgKwogIGxhYnModGl0bGUgPSBwYXN0ZTAoJ0dlbmUgRmFtaWxpZXMgRW5yaWNoZWQgaW4gJywgCiAgICAgICAgICAgICAgICAgICAgICBnc3ViKCctJywnIHZzICcsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hbWVzKGxpc3QubXlUb3BIaXRzLmRmKVtbeXldXSkpLAogICAgICAgc3VidGl0bGUgPSAnTkVTID0gTm9ybWFsaXplZCBFbnJpY2htZW50IFNjb3JlOyBHZW5lIGZhbWlseSBhc3NpZ25tZW50cyAKICAgICAgICAgICAgIGZyb20gRW5zZW1ibCBDb21wYXJhIGRhdGFzZXQgZGVmaW5lZCBpbiBIdW50IGV0IGFsIDIwMTYnLAogICAgICAgeCA9ICJMaWZlIFN0YWdlIiwKICAgICAgIHkgPSAiRmFtaWx5IElEIikgKwogICNjb29yZF9maXhlZCgxLzIpICsKICB0aGVtZV9idygpICsKICB0aGVtZShwbG90LnRpdGxlLnBvc2l0aW9uID0gInBsb3QiLAogICAgICAgIHBsb3QuY2FwdGlvbi5wb3NpdGlvbiA9ICJwbG90IiwKICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGZhY2UgPSAiYm9sZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaXplID0gMTMsIGhqdXN0ID0gMCksCiAgICAgICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChmYWNlID0gImJvbGQiLHNpemUgPSAxMC40KSwKICAgICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KGZhY2U9ImJvbGQiLHNpemUgPSAxMC40KSwKICAgICAgICBhc3BlY3QucmF0aW8gPSAzLzEpCgpgYGAKCgoK